ch19函数的高级话题(python)

#函数设计概念
耦合性:对于输入使用参数,对于输出使用return语句
耦合性:只有在真正必要的情况下使用全局变量
耦合性:不要改变可变类型的参数,除非调用者希望如此。
聚合性:每一个函数都应该有一个单一的,统一的目标
大小:每一个函数应该相对较小
耦合性:避免直接改变在另一个模块文件中的变量。

#使函数和其他编程组件中的外部依赖最小化。

#利用递归求和
>>> def mysum(L):
...     if not L:
...         return 0
...     else:
...         return L[0] + mysum(L[1:])           # Call myself

>>> mysum([1, 2, 3, 4, 5])
15


#输出L查看
>>> def mysum(L):
...     print(L)                                 # Trace recursive levels
...     if not L:                                # L shorter at each level
...         return 0
...     else:
...         return L[0] + mysum(L[1:])
...
>>> mysum([1, 2, 3, 4, 5])
[1, 2, 3, 4, 5]
[2, 3, 4, 5]
[3, 4, 5]
[4, 5]
[5]
[]
15


#编码替代方案,使用if/else表达式
def mysum(L):
    return 0 if not L else L[0] + mysum(L[1:])           # Use ternary expression

def mysum(L):
    return L[0] if len(L) == 1 else L[0] + mysum(L[1:])  # Any type, assume one

def mysum(L):  #可以传入任何可迭代对象
    first, *rest = L
    return first if not rest else first + mysum(rest)    # Use 3.0 ext seq assign


#支持+的任何对象都可以当参数传入
>>> mysum([1])                              # mysum([]) fails in last 2
1
>>> mysum([1, 2, 3, 4, 5])
15
>>> mysum(('s', 'p', 'a', 'm'))             # But various types now work
'spam'
>>> mysum(['spam', 'ham', 'eggs'])
'spamhameggs'


#递归也可以间接调用自身(这里通过nonempty函数调用自身)
>>> def mysum(L):
...     if not L: return 0
...     return nonempty(L)                  # Call a function that calls me
...
>>> def nonempty(L):
...     return L[0] + mysum(L[1:])          # Indirectly recursive
...
>>> mysum([1.1, 2.2, 3.3, 4.4])
11.0


#循环语句VS递归

>>> L = [1, 2, 3, 4, 5]
>>> sum = 0
>>> while L:
...     sum += L[0]
...     L = L[1:]
...
>>> sum
15


#for迭代
>>> L = [1, 2, 3, 4, 5]
>>> sum = 0
>>> for x in L: sum += x
...
>>> sum
15

#递归消耗时间和堆栈空间


#递归处理任意结构

#求这个列表的和
[1, [2, [3, 4], 5], 6, [7, 8]]                   # Arbitrarily nested sublists



def sumtree(L):
    tot = 0
    for x in L:                                  # For each item at this level
        if not isinstance(x, list):
            tot += x                             # Add numbers directly
        else:
            tot += sumtree(x)                    # Recur for sublists
    return tot

L = [1, [2, [3, 4], 5], 6, [7, 8]]               # Arbitrary nesting
print(sumtree(L))                                # Prints 36


# Pathological cases

print(sumtree([1, [2, [3, [4, [5]]]]]))          # Prints 15 (right-heavy)

print(sumtree([[[[[1], 2], 3], 4], 5]))          # Prints 15 (left-heavy)

#类中的一些运算符重载方法,例如__setattr__和__getattribute__,如果使用不正确的话,都有潜在的可能会递归的循环。


#函数对象:属性和注解

#函数是对象,自身存储在内存块中

#间接函数调用
#在def运行之后,函数名直接是一个对象的引用

>>> def echo(message):                   # Name echo assigned to function object
...     print(message)
...
>>> echo('Direct call')                  # Call object through original name
Direct call

>>> x = echo                             # Now x references the function too
>>> x('Indirect call!')                  # Call object through name by adding ()
Indirect call!


#由于参数通过赋值对象来传递,这就像把函数作为参数传递给其他函数一样容易
>>> def indirect(func, arg):
...     func(arg)                        # Call the passed-in object by adding ()
...
>>> indirect(echo, 'Argument call!')     # Pass the function to another function
Argument call!


把函数对象填入数据结构中
>>> schedule = [ (echo, 'Spam!'), (echo, 'Ham!') ]
>>> for (func, arg) in schedule:
...     func(arg)                        # Call functions embedded in containers
...
Spam!
Ham!


#一个函数可以返回一个函数对象,嵌套函数
>>> def make(label):                     # Make a function but don't call it
...     def echo(message):
...         print(label + ':' + message)
...     return echo
...
>>> F = make('Spam')                     # Label in enclosing scope is retained
>>> F('Ham!')                            # Call the function that make returned
Spam:Ham!
>>> F('Eggs!')
Spam:Eggs!


#函数内省
>>> def func(a):
...     b = 'spam'
...     return b * a
...
>>> func(8)
'spamspamspamspamspamspamspamspam'


#函数内省
>>> func.__name__
'func'
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
...more omitted...
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']


#函数附加的code对象包含了函数的本地变量和参数等方面的细节
>>> func.__code__
<code object func at 0x0257C9B0, file "<stdin>", line 1>

>>> dir(func.__code__)
['__class__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__',
...more omitted...
'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename',
'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab',
'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']

>>> func.__code__.co_varnames
('a', 'b')
>>> func.__code__.co_argcount
1


#函数属性

#函数不仅仅有前面的系统定义属性,也可以添加自定义属性
>>> func
<function func at 0x0257C738>
>>> func.count = 0
>>> func.count += 1
>>> func.count
1
>>> func.handles = 'Button-Press'
>>> func.handles
'Button-Press'
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
...more omitted...
__str__', '__subclasshook__', 'count', 'handles']

#这样的属性可以直接用来把状态信息附加到函数对象,而不必使用全局,非本地和类等其他技术。
#这也是模拟其他语言的"静态本地变量"的一种方式,这种变量的名称对于一个函数来说是本地的,其值在函数退出后还保留。属性与对象相关而不是与作用域相关。


#python3.0中的函数注解

#函数注解是与函数参数和返回值相关的任意的用户定义的数据
#函数注解会附加到函数对象的__annotations__属性上以供其他用户使用

#不带函数注解
>>> def func(a, b, c):
...     return a + b + c
...
>>> func(1, 2, 3)
6


#带有函数注解的函数头
>>> def func(a: 'spam', b: (1, 10), c: float) -> int:
...     return a + b + c
...
>>> func(1, 2, 3)
6


#查看函数注解
>>> func.__annotations__
{'a': 'spam', 'c': <class 'float'>, 'b': (1, 10), 'return': <class 'int'>}


#a,b带有函数注解
>>> def func(a: 'spam', b, c: 99):
...     return a + b + c
...
>>> func(1, 2, 3)
6

#遍历函数注解  func.__annotations__返回的就是字典
>>> func.__annotations__
{'a': 'spam', 'c': 99}

>>> for arg in func.__annotations__:
...    print(arg, '=>', func.__annotations__[arg])
...
a => spam
c => 99


#即使有函数注解,还是可以有默认值
>>> def func(a: 'spam' = 4, b: (1, 10) = 5, c: float = 6) -> int:
...     return a + b + c
...
>>> func(1, 2, 3)
6
>>> func()                       # 4 + 5 + 6   (all defaults)
15
>>> func(1, c=10)                # 1 + 5 + 10  (keywords work normally)
16
>>> func.__annotations__
{'a': 'spam', 'c': <class 'float'>, 'b': (1, 10), 'return': <class 'int'>}


#提高代码可读性
>>> def func(a:'spam'=4, b:(1,10)=5, c:float=6)->int:
...     return a + b + c
...
>>> func(1, 2)                   # 1 + 2 + 6
9
>>> func.__annotations__
{'a': 'spam', 'c': <class 'float'>, 'b': (1, 10), 'return': <class 'int'>}

#注解只对def有效,对lambda无效


#匿名函数:lambda
#lambda创建了一个之后能够调用的函数,返回了一个函数对象
#lambada表达式形式:
lambda argument1,argument2,...:expression using arguments

#lambda是一个表达式,不是语句。能出现在def不能出现的地方。
#lambda的主体是一个单个的表达式,而不是一个代码块。将结果写成一个顺畅的表达式。连if语句都不能使用。lambda为编写简单的函数而设计。

#def创建的函数
>>> def func(x, y, z): return x + y + z
...
>>> func(2, 3, 4)
9


#使用lambda表达式
>>> f = lambda x, y, z: x + y + z
>>> f(2, 3, 4)
9


#lambda也能够使用默认参数
>>> x = (lambda a="fee", b="fie", c="foe": a + b + c)
>>> x("wee")
'weefiefoe'


#lambda主体中的代码象在def内的代码一样都遵循相同的作用域查找法则LEGB。
>>> def knights():
...     title = 'Sir'
...     action = (lambda x: title + ' ' + x)        # Title in enclosing def
...     return action                               # Return a function
...
>>> act = knights()
>>> act('robin')
'Sir robin'


#为什么使用lambda

#lambda写在常量表达式中,这种常量表达式中不可嵌入def
L = [lambda x: x ** 2,               # Inline function definition
     lambda x: x ** 3,
     lambda x: x ** 4]               # A list of 3 callable functions

for f in L:
    print(f(2))                      # Prints 4, 8, 16

print(L[0](3))                       # Prints 9


#def的形式
def f1(x): return x ** 2
def f2(x): return x ** 3             # Define named functions
def f3(x): return x ** 4

L = [f1, f2, f3]                     # Reference by name

for f in L:
    print(f(2))                      # Prints 4, 8, 16

print(L[0](3))                       # Prints 9


#lambda作为字典的值存在
>>> key = 'got'
>>> {'already': (lambda: 2 + 2),
...  'got':     (lambda: 2 * 4),
...  'one':     (lambda: 2 ** 6)}[key]()
8


#def形式
>>> def f1(): return 2 + 2
...
>>> def f2(): return 2 * 4
...
>>> def f3(): return 2 ** 6
...
>>> key = 'one'
>>> {'already': f1, 'got': f2, 'one': f3}[key]()
64

#lambda主体必须是单个表达式(而不是语句),所以仅能将有限的逻辑封装到以恶搞lambda中。
#在lambda中进行print,直接编写sys.stdout.write(str(x)+'\n'),而不是使用print(x)


#在lambda中写逻辑嵌套的时候,使用if/else三元表达式或者对等的and/or组合
>>> lower = (lambda x, y: x if x < y else y)
>>> lower('bb', 'aa')
'aa'
>>> lower('aa', 'bb')
'aa'


如果要在lambda中执行循环,能够潜入map调用或者列表解析表达式。
>>> import sys
>>> showall = lambda x: list(map(sys.stdout.write, x))        # Use list() in 3.0

>>> t = showall(['spam\n', 'toast\n', 'eggs\n'])
spam
toast
eggs


>>> showall = lambda x: [sys.stdout.write(line) for line in x]

>>> t = showall(('bright\n', 'side\n', 'of\n', 'life\n'))
bright
side
of
life

#上面的代码容易造成晦涩难懂。


#lambda出现在def中,通过LEGB法则可以获取上层函数作用域中x的值
>>> def action(x):
...     return (lambda y: x + y)         # Make and return function, remember x
...
>>> act = action(99)
>>> act
<function <lambda> at 0x00A16A88>
>>> act(2)                               # Call what action returned
101


#lambda嵌套lambda的形式,第二个lambda能访问x,通过LEGB
>>> action = (lambda x: (lambda y: x + y))
>>> act = action(99)
>>> act(3)
102
>>> ((lambda x: (lambda y: x + y))(99))(4)
103


#lambda的另一个常用的应用就是Python的tkinter GUI API(python2.6中是Tkinter)

import sys
from tkinter import Button, mainloop     # Tkinter in 2.6
x = Button(
        text ='Press me',
        command=(lambda:sys.stdout.write('Spam\n')))
x.pack()
mainloop()


#因为嵌套的作用域法则对lambda有效,使回调处理变得简单易用
class MyGui:
    def makewidgets(self):
        Button(command=(lambda: self.onPress("spam")))
    def onPress(self, message):
        ...use message...



>>> counters = [1, 2, 3, 4]
>>>
>>> updated = []
>>> for x in counters:
...     updated.append(x + 10)                 # Add 10 to each item
...
>>> updated
[11, 12, 13, 14]


#上面例子的map版本:map函数会对序列对象中的每一个元素应用被传入的函数,并且返回一个包含了所有函数调用结果的一个列表
>>> def inc(x): return x + 10                  # Function to be run
...
>>> list(map(inc, counters))                   # Collect results
[11, 12, 13, 14]


#使用lambda
>>> list(map((lambda x: x + 3), counters))     # Function expression
[4, 5, 6, 7]


#自己的映射工具函数
>>> def mymap(func, seq):
...     res = []
...     for x in seq: res.append(func(x))
...     return res


#测试上面的自己的映射工具函数
>>> list(map(inc, [1, 2, 3]))             # Built-in is an iterator
[11, 12, 13]
>>> mymap(inc, [1, 2, 3])                 # Ours builds a list (see generators)
[11, 12, 13]


#提供多个序列
>>> pow(3, 4)                             # 3**4
81
>>> list(map(pow, [1, 2, 3], [2, 3, 4]))  # 1**2, 2**3, 3**4
[1, 8, 81]

#对于多个序列,map期待N参数的函数用于N序列


#函数式编程工具:filter和reduce
#函数式编程的意思是对序列应用一些函数的工具

>>> list(range(-5, 5))                                   # An iterator in 3.0
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> list(filter((lambda x: x > 0), range(-5, 5)))        # An iterator in 3.0
[1, 2, 3, 4]


#等效的for实现
>>> res = []
>>> for x in range(-5, 5):
...     if x > 0:
...         res.append(x)
...
>>> res
[1, 2, 3, 4]



#reduce在python2.6是一个内置函数,在python3中位于functools模块中

>>> from functools import reduce      # Import in 3.0, not in 2.6

>>> reduce((lambda x, y: x + y), [1, 2, 3, 4])
10
>>> reduce((lambda x, y: x * y), [1, 2, 3, 4])
24



>>> L = [1,2,3,4]
>>> res = L[0]
>>> for x in L[1:]:
...     res = res + x
...
>>> res
10


#自己的reduce版本
>>> def myreduce(function, sequence):
...     tally = sequence[0]
...     for next in sequence[1:]:
...         tally = function(tally, next)
...     return tally
...
>>> myreduce((lambda x, y: x + y), [1, 2, 3, 4, 5])
15
>>> myreduce((lambda x, y: x * y), [1, 2, 3, 4, 5])
120



>>> import operator, functools
>>> functools.reduce(operator.add, [2, 4, 6])        # Function-based +
12
>>> functools.reduce((lambda x, y: x + y), [2, 4, 6])
12



评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用