VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • 一步步教你理解Python装饰器(3)

这个例子对你来说可能也不是太奇怪——add和sub是标准的Python函数,它们都接受两个值并返回一个计算了的结果。在#1处你可以看到变量接受一个函数就像其它任何普通的变量。在#2处我们调用传入apply的函数——在Python里双括号是调用操作符,并且调用变量名包含的值。在#3处你可以看出在Python中将函数当做值进行传递并没有任何特殊语法——函数名就像任何其它变量一样只是变量标签。

 

你之前可能见过这种行为——Python将函数作为参数经常见于像通过为key参数提供一个函数来自定义sorted内建函数等操作中。但是,将函数作为返回值返回会怎样呢?请考虑:

1
2
3
4
5
6
7
8
9
10
>>> def outer():
...     def inner():
...         print "Inside inner"
...     return inner # 1
...
>>> foo = outer() #2
>>> foo # doctest:+ELLIPSIS
<function inner at 0x...>
>>> foo()
Inside inner

这乍看起来有点奇怪。在#1处我返回了变量inner,它碰巧是一个函数标签。这里没有特殊语法——我们的函数返回了inner函数(调用outer()函数并不产生可见的执行)。还记得变量的生命周期吗?每当outer函数被调用时inner函数就会重新被定义一次,但是如果inner函数不被(outer)返回那么当超出outer的作用域后,inner将不复存在了。

 

在#2处我们可以获取到返回值,它是我们的inner函数,它被存储于一个新的变量foo。我们可以看到,如果我们计算foo,它真的包含inner函数,我们可以通过使用调用运算符(双括号,还记得吗?)来调用它。这看起来可能有点怪异,但是到目前为止没有什么难以理解,不是么?挺住,因为接下来的东西将会很怪异。

 

8、闭包(Closures)

让我们不从定义而是从另一个代码示例开始。如果我们将上一个例子稍加修改会怎样呢?

1
2
3
4
5
6
7
8
>>> def outer():
...     x = 1
...     def inner():
...         print x # 1
...     return inner
>>> foo = outer()
>>> foo.func_closure # doctest: +ELLIPSIS
(<cell at 0x...: int object at 0x...>,)

从上一个例子中我们看到inner是一个由outer返回的函数,存储于一个名为foo的变量,我们可以通过foo()调用它。但是它能运行吗?让我们先来考虑一下作用域规则。

 

一切都依照Python的作用域规则而运行——x是outer函数了一个local变量。当inner在#1处打印x时,Python在inner中寻找一个local变量,没有找到;然后它在外层作用域即outer函数中寻找并找到了它。

 

但是自此处从变量生命周期的角度来看又会如何呢?变量x是函数outer的local变量,这意味着只有当outer函数运行时它才存在。只有当outer返回后我们才能调用inner,因此依照我们关于Python如何运作的模型来看,在我们调用inner的时候x已经不复存在了,那么某个运行时错误可能会出现。

 

事实与我们的预想并不一致,返回的inner函数的确正常运行。Python支持一种称为闭包(function closures)的特性,这意味着定义于非全局作用域的inner函数在定义时记得它们的外层作用域长什么样。这可以通过查看inner函数的func_closure属性来查看,它包含了外层作用域里的变量。

 

请记住,每次当outer函数被调用时inner函数都被重新定义一次。目前x的值没有改变,因此我们得到的每个inner函数和其它的inner函数拥有相同的行为,但是如果我们将它做出一点改变呢?

1
2
3
4
5
6
7
8
9
10
>>> def outer(x):
...     def inner():
...         print x # 1
...        return inner
>>> print1 = outer(1)
>>> print2 = outer(2)
>>> print1()
1
>>> print2()
2

从这个例子中你可以看到closures——函数记住他们的外层作用域的事实——可以用来构建本质上有一个硬编码参数的自定义函数。我们没有将数字1或者2传递给我们的inner函数但是构建了能"记住"其应该打印数字的自定义版本。

 

closures就是一个强有力的技术——你甚至想到在某些方面它有点类似于面向对象技术:outer是inner的构造函数,x扮演着一个类似私有成员变量的角色。它的作用有很多,如果你熟悉Python的sorted函数的key参数,你可能已经写过一个lambda函数通过第二项而不是第一项来排序一些列list。也许你现在可以写一个itemgetter函数,它接收一个用于检索的索引并返回一个函数,这个函数适合传递给key参数。

 

但是让我们不要用闭包做任何噩梦般的事情!相反,让我们重新从头开始来写一个decorator!

 

9、装饰器(Decorators)

一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包。我们将从简单的开始一直到写出有用的decorators。

1
2
3
4
5
6
7
8
9
10
11
12
>>> def outer(some_func):
...        def inner():
...            print "before some_func"
...            ret = some_func() # 1
...            return ret + 1
...        return inner
>>> def foo():
...        return 1
>>> decorated = outer(foo) # 2
>>> decorated()
before some_func
2

相关教程