跳转至

装饰器

装饰器是一个可调用对象,接受一个函数作为参数并返回一个函数,如下定义了一个装饰器:

>>> def decorator(x):
...     print(x)
...     return x
...
>>>

装饰器在函数定义前使用,@标记一个装饰器:

>>> @decorator    
... def target(x):
...     return x + 1
... 
<function target at ...>
>>>

@decorator等价于如下代码,函数被装饰器返回的函数替换

>>> def target(x):
...     return x + 1
... 
>>> target = decorator(target)
<function target at ...>
>>>

装饰器在被修饰的函数定义后立即运行。因此,当导入模块时,装饰器函数会立即执行,而被装饰的函数只会调用执行。

>>> src = """def decorator(_):
...     print(_)
...     return _
... print("Before A")
... @decorator
... def a():
...     return 1
... print("Before B")
... @decorator  
... def b():
...     return 1
... print("After B")
... """
>>> exec(src)
Before A
<function a at ...>
Before B
<function b at ...>
After B
>>>

变量的作用域

Python会自动判断函数代码中的变量是否为局部变量。所有在函数代码内出现的变量被视为局部变量。

>>> b = 2
>>> def A():
...     print(b)
... 
>>> def B():
...     print(b)
...     b = 2
... 
>>> A()
2
>>> B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in B
UnboundLocalError: local variable 'b' referenced before assignment
>>>

因此,如果需要在函数内部访问全局变量,需要使用global关键字声明。

>>> def C():
...     global b
...     print(b)
...     b = 3
... 
>>> b
2
>>> C()
2
>>> b
3
>>>

检查编译后的字节码,发现global关键字声明变量由LOAD_FASTSTORE_FAST改为了LOAD_GLOBALSTORE_GLOBAL

闭包

闭包是扩展了函数作用域的函数,可以访问函数定义之外的局部变量。以下通过不同方式实现滑动平均值的计算:

  • 使用类
>>> class avg1():
...     def __init__(self):
...             self.values = []
...     def __call__(self, value):
...             self.values.append(value)
...             return sum(self.values) / len(self.values)
... 
>>> avg = avg1()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>>
  • 使用高阶函数
>>> def avg2():
...     values = []
...     def avg(value): 
...             values.append(value)
...             return sum(values) / len(values)
...     return avg
>>> 
>>> avg = avg2()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>>

avg2函数结束后,局部变量values原有的作用域消失,成为自由变量。自由变量指没有在本地作用域中绑定的变量。values变量的引用存储在avg函数的__closure__属性中。即使avg2函数已经结束执行,avg函数仍然能访问values变量。

评论