Bootstrap

Chapter 19:Advanced Function Topics

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
;