Skip to content

Fluent Python读书笔记(六)

18965050 edited this page Jan 9, 2017 · 2 revisions

基于函数的设计模式

策略模式

经典的策略模式实现, 参见: 06-dp-1class-func/classic_strategy.py

由于在Python中, 函数就是对象, 因此可以省去策略接口, 直接使用函数对象.

函数式的策略模式实现, 参见: 06-dp-1class-func/strategy.py

装饰器和闭包

装饰器

关于装饰器,请记住:

  • 装饰器函数能增加或替换被装饰函数

  • 装饰器函数在模块加载时, 被装饰函数定义的时候就开始执行了

  • 在定义装饰器函数时, 如果忘了添加functools.wraps装饰器, 它会将被装饰函数的元信息拷贝过来

     registry = []  # <1>
     
     def register(func):  # <2>
         print('running register(%s)' % func)  # <3>
         registry.append(func)  # <4>
         return func  # <5>
     
     @register  # <6>
     def f1():
         print('running f1()')
     
     @register
     def f2():
         print('running f2()')
     
     def f3():  # <7>
         print('running f3()')
     
     def main():  # <8>
         print('running main()')
         print('registry ->', registry)
         f1()
         f2()
         f3()
     
     if __name__=='__main__':
         main()  # <9>
     
     # 输出
     running register(<function f1 at 0x00000000011301E0>)
     running register(<function f2 at 0x0000000001130268>)
     running main()
     registry -> [<function f1 at 0x00000000011301E0>, <function f2 at 0x0000000001130268>]
     running f1()
     running f2()
     running f3()

变量作用域规则

>>> b = 6
>>> def f2(a):
... print(a)
... print(b)
... b = 9
...
>>> f2(3)
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f2
UnboundLocalError: local variable 'b' referenced before assignment

上述代码中, 由于在函数内定义了b, 因此函数内b的作用域为local. 又因为b定义在print语句后, 因此抛出了异常. 如果要使用全局的b, 使用global关键字.

闭包

要设计一个求平均值的函数, 采用类设计方式, 为:

"""
>>> avg = Averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0

"""


class Averager():

    def __init__(self):
        self.series = []

    def __call__(self, new_value):
        self.series.append(new_value)
        total = sum(self.series)
        return total/len(self.series)

如果采用闭包设计, 为:

"""
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
>>> avg.__code__.co_varnames
('new_value', 'total')
>>> avg.__code__.co_freevars
('series',)
>>> avg.__closure__  # doctest: +ELLIPSIS
(<cell at 0x...: list object at 0x...>,)
>>> avg.__closure__[0].cell_contents
[10, 11, 12]
"""

DEMO = """
>>> avg.__closure__
(<cell at 0x107a44f78: list object at 0x107a91a48>,)
"""


def make_averager():
    series = []			#说明此local变量的作用域并不限制在make_averager函数内

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager

nonlocal关键词

先看段代码

def make_averager():
	count = 0
	total = 0

	def averager(new_value):
		count += 1
		total += new_value
		return total / count

	return averager

# 运行
avg = make_averager()
avg(10)				# UnboundLocalError: local variable 'count' referenced before assignment

上述代码之所以不能运行, 是由于count += 1等价于count=count+1. 此时count就变成averager()函数的local变量了, 由于count没有在averager()函数中定义, 因此报错. 这种情况下就需要使用nonlocal关键字

适配多个装饰器

@d1
@d2
def f():
	print('f')
```

等价于
```python
def f():
	print('f')
f = d1(d2(f))
```

### 给装饰器添加其他参数
使用一个装饰器工厂返回一个装饰器, 并在此装饰器中装饰被装饰对象
```python
registry = set()  # <1>

def register(active=True):  # <2>
    def decorate(func):  # <3>
        print('running register(active=%s)->decorate(%s)'
              % (active, func))
        if active:   # <4>
            registry.add(func)
        else:
            registry.discard(func)  # <5>

        return func  # <6>
    return decorate  # <7>

@register(active=False)  # <8>		等价于 register(active=False)(f1)
def f1():
    print('running f1()')

@register()  # <9>
def f2():
    print('running f2()')

def f3():
    print('running f3()')
```