Learning Python 第五版 英文版
Chapter 19:Advanced Function Topics
主要内容:
- 递归函数
- 函数属性与注释
- lambda表达式
- 函数编程工具,例如map和filter。
1. 递归函数
1.1简单递归
递归函数可以直接或非直接的调用自己
>>> def mysum(L):
if not L:
return 0
else:
return L[0] + mysum(L[1:]) # Call myself recursively
#仅限数字求和
>>> mysum([1, 2, 3, 4, 5])
15
可以用if/else三元操作符代替
>>> def mysum(L):
return 0 if not L else L[0] + mysum(L[1:]) # Use ternary expression
#仅限数字求和
>>> mysum([1, 2, 3, 4, 5])
15
可以为任意支持+操作的类型求和
>>> def mysum(L):
return L[0] if len(L) == 1 else L[0] + mysum(L[1:]) # Any type, assume one
#但是不能传入空值,如[],(),''
>>> mysum([1, 2, 3, 4, 5])
15
>>> mysum(('s', 'p', 'a', 'm'))
'spam'
最新的赋值语法
>>> def mysum(L):
first, *rest = L
return first if not rest else first + mysum(rest) # Use 3.X ext seq assign
#但是不能传入空值,如[],(),''
>>> mysum([1, 2, 3, 4, 5])
15
>>> mysum(['spam', 'ham', 'eggs'])
'spamhameggs'
上面的例子都是直接调用自己,下面是一个非直接的调用自己
>>> 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
1.2 循坏语句 VS 递归
上面的例子中都用到了递归,但是山鸡焉用在牛刀。递归在Python中用的不多,人们更喜欢用简单粗暴的循环语句
while循环
>>> 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.3 处理任意结构
处理任意嵌套的子列表
[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)
1.4 递归 VS 队列与栈
广度优先算法,显式的队列,先进先出
def sumtree(L): # Breadth-first,广度优先 explicit queue
tot = 0
items = list(L) # Start with copy of top level
while items:
front = items.pop(0) # Fetch/delete front item
if not isinstance(front, list):
tot += front # Add numbers directly
else:
items.extend(front) # <== Append all in nested list
return tot
深度优先算法,显式的栈,后进先出
def sumtree(L): # Depth-first,深度优先 explicit stack
tot = 0
items = list(L) # Start with copy of top level
while items:
front = items.pop(0) # Fetch/delete front item
if not isinstance(front, list):
tot += front # Add numbers directly
else:
items[:0] = front # <== Prepend all in nested list
return tot
L = [1, [2, [3, 4], 5], 6, [7, 8]] # Arbitrary nesting
print(sumtree(L))
print(sumtree([1, [2, [3, [4, [5]]]]]))
print(sumtree([[[[[1], 2], 3], 4], 5]))
output:
#广度优先
----------------------------------------
1, 6, 2, 5, 7, 8, 3, 4, 36
1, 2, 3, 4, 5, 15
5, 4, 3, 2, 1, 15
----------------------------------------
#深度优先
----------------------------------------
1, 2, 3, 4, 5, 6, 7, 8, 36
1, 2, 3, 4, 5, 15
1, 2, 3, 4, 5, 15
----------------------------------------
1.5 循环,路径和栈限制
对于大型递归应用,就需要一些基本措施来阻止无限循环或重复,记录路径,扩展栈空间。
如果待处理数据是个循环数据(周期循环),上面的例子就失败了。
当递归运行的时候,我们应该保持一个访问过的状态的set,dictionary或list来检查是否重复
if state not in visited:
visited.add(state) # x.add(state), x[state]=True, or x.append(state)
...proceed...
visited.add(front)
...proceed...
items.extend([x for x in front if x not in visited])
有些程序也许需要记录完整的状态访问的路径。
Python限制运行时调用栈的深度,避免无限递归循环。使用sys模块,可以扩展它:
>>> sys.getrecursionlimit() # 1000 calls deep default
1000
>>> sys.setrecursionlimit(10000) # Allow deeper nesting
>>> help(sys.setrecursionlimit) # Read more about it
2 函数对象:属性和注释
2.1 非直接函数调用:“First Class” Objects
>>> 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!
2.2 函数内省
>>> def func(a):
b = 'spam'
return b * a
>>> func(8)
'spamspamspamspamspamspamspamspam'
>>> func.__name__
'func'
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
Function Objects: Attributes and Annotations | 563
...more omitted: 34 total...
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> func.__code__
<code object func at 0x00000000021A6030, file "<stdin>", line 1>
>>> dir(func.__code__)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
...more omitted: 37 total...
'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
2.3 函数属性
>>> func
<function func at 0x000000000296A1E0>
>>> func.count = 0
>>> func.count += 1
>>> func.count
1
>>> func.handles = 'Button-Press'
>>> func.handles
'Button-Press'
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
...and more: in 3.X all others have double underscores so your names won't clash...
__str__', '__subclasshook__', 'count', 'handles']
3 匿名函数:lambda
3.1 lambda基本语法
lambda argument1, argument2,... argumentN : expression using arguments
lambda是一个表达式,不是一个语句。lambda整体就是一个表达式,并不是一些语句的组合。
>>> def func(x, y, z): return x + y + z
>>> func(2, 3, 4)
9
>>> f = lambda x, y, z: x + y + z
>>> f(2, 3, 4)
9
>>> x = (lambda a="fee", b="fie", c="foe": a + b + c)
>>> x("wee")
'weefiefoe'
>>> def knights():
title = 'Sir'
action = (lambda x: title + ' ' + x) # Title in enclosing def scope
return action # Return a function object
>>> act = knights()
>>> msg = act('robin') # 'robin' passed to x
>>> msg
'Sir robin'
>>> act # act: a function, not its result
<function knights.<locals>.<lambda> at 0x00000000029CA488>
L = [lambda x: x ** 2, # Inline function definition
lambda x: x ** 3,
lambda x: x ** 4] # A list of three callable functions
for f in L:
print(f(2)) # Prints 4, 8, 16
print(L[0](3)) # Prints 9
>>> key = 'got'
>>> {'already': (lambda: 2 + 2),
'got': (lambda: 2 * 4),
'one': (lambda: 2 ** 6)}[key]()
8
>>> lower = (lambda x, y: x if x < y else y)
>>> lower('bb', 'aa')
'aa'
>>> lower('aa', 'bb')
'aa
>>> import sys
>>> showall = lambda x: list(map(sys.stdout.write, x)) # 3.X: must use list
>>> t = showall(['spam\n', 'toast\n', 'eggs\n']) # 3.X: can use print
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
>>> showall = lambda x: [print(line, end='') for line in x] # Same: 3.X only
>>> showall = lambda x: print(*x, sep='', end='') # Same: 3.X only
>>> def action(x):
return (lambda y: x + y) # Make and return function, remember x
>>> act = action(99)
>>> act
<function action.<locals>.<lambda> at 0x00000000029CA2F0>
>>> act(2) # Call what action returned
101
>>> action = (lambda x: (lambda y: x + y))
>>> act = action(99)
>>> act(3)
102
>>> ((lambda x: (lambda y: x + y))(99))(4)
103
4 函数编程工具
4.1 map
>>> counters = [1, 2, 3, 4]
>>>
>>> updated = []
>>> for x in counters:
updated.append(x + 10) # Add 10 to each item
>>> updated
[11, 12, 13, 14]
>>> def inc(x): return x + 10 # Function to be run
>>> list(map(inc, counters)) # Collect results
[11, 12, 13, 14]
>>> list(map((lambda x: x + 3), counters)) # Function expression
[4, 5, 6, 7]
>>> list(map(pow, [1, 2, 3], [2, 3, 4])) # 1**2, 2**3, 3**4
[1, 8, 81]
>>> list(map(inc, [1, 2, 3, 4]))
[11, 12, 13, 14]
>>> [inc(x) for x in [1, 2, 3, 4]] # Use () parens to generate items instead
[11, 12, 13, 14]
4.2 filter
>>> list(range(−5, 5)) # An iterable in 3.X
[−5, −4, −3, −2, −1, 0, 1, 2, 3, 4]
>>> list(filter((lambda x: x > 0), range(−5, 5))) # An iterable in 3.X
[1, 2, 3, 4]
>>> [x for x in range(−5, 5) if x > 0] # Use () to generate items
[1, 2, 3, 4]
4.3 reduce
>>> from functools import reduce # Import in 3.X, not in 2.X
>>> reduce((lambda x, y: x + y), [1, 2, 3, 4])
10
>>> reduce((lambda x, y: x * y), [1, 2, 3, 4])
24
>>> import operator, functools
>>> functools.reduce(operator.add, [2, 4, 6]) # Function-based +
12
>>> functools.reduce((lambda x, y: x + y), [2, 4, 6])
12