首页 > Python基础教程 >
-
一步步教你理解Python装饰器
或许你已经用过装饰器,它的使用方式非常简单但理解起来困难(其实真正理解的也很简单),想要理解装饰器,你需要懂点函数式编程的概念,python函数的定义以及函数调用的语法规则等,虽然我没法把装饰器变得简单,但是我希望可以通过下面的步骤让你由浅入深明白装饰器是什么。假定你拥有最基本的Python知识,本文阐述的东西可能对那些在工作中经常接触Python的人有很大的帮助。下面我们来一步步理解python的装饰器:
1、函数(Functions)
在Python里,函数是用def关键字后跟一个函数名称和一个可选的参数表列来创建的,可以用关键字return指定返回值。下面让我们创建和调用一个最简单的函数:
1
2
3
4
|
>>> def foo(): ... return 1 >>> foo() 1 |
该函数的函数体(在Python里将就是多行语句)是强制性的并且通过缩进来表明。我们可以通过在函数名后面添加双括号来调用函数。
2、作用域(Scope)
在Python中,每个函数都会创建一个作用域。Pythonistas也可能称函数拥有它们自己的命名空间(namespace)。这意味着当在函数体里遇到变量名时,Python首先在该函数的命名空间中查找,Python包含了一些让我们查看命名空间的函数。让我们写一个简单的函数来探查一下local和global作用域的区别。
1
2
3
4
5
6
7
|
>>> a_string = "This is a global variable" >>> def foo(): ... print locals() >>> print globals() # doctest: +ELLIPSIS {..., 'a_strin' : 'This ia a global variable' } >>> foo() # 2 {} |
内建的globals函数返回一个字典对象,它包含所有Python知道的变量名(为了清楚明了起见,我已经忽略了一些Python自动创建的变量)。在#2处我调用了函数foo,它将函数内部的local namespace里的内容打印了出来。正如我们看到的foo函数拥有自己的独立namespace,现在它还是空的。
3、变量解析规则(variable resolution rules)
当然,这并不意味着在函数内部我们不能访问全局变量。Python的作用域规则是,变量的创建总会创建一个新的local变量,但是变量的访问(包括修改)会先查找local作用域然后顺着最邻近的作用域去寻找匹配。因此,如果我们修改foo函数来让它打印global变量,结果就会像我们希望的那样:
1
2
3
4
5
|
>>> a_string = "This is global variable" >>> def foo(): ... print a_string # 1 >>> foo() This is a global variable |
在#1处,Python在函数中寻找一个local变量,但是没有找到,然后在global变量中找到了一个同名的变量。
另一方面,如果我们尝试在函数里给global变量赋值,结果将不如我们所愿:
1
2
3
4
5
6
7
8
|
>>> a_string = 'This is a global variable" >>> def foo(): ... a_string = "test" # 1 ... print locals() >>> foo() { 'a_string' : 'test' } >>> a_string # 2 'This is a global variable' |
正如我们所见,全局变量可以被访问到(如果是可变类型,其甚至可以被改变),但是(默认情况下)不能被赋值。在函数内部的#1处我们实际上创建了一个新的local变量,它和全局变量拥有相同的名字,它将全局变量给覆盖了。我们可以通过在foo函数内部打印local namespace来发现到它已经有了一个条目,通过对函数外部的#2处的输出结果我们可以看到,变量a_string的值根本就没有被改变。
4、变量的生命周期(Variable lifetime)
也要注意到,变量不仅“生活在”一个命名空间里,它们还有生命周期。考虑下面的代码:
1
2
3
4
5
6
7
|
>>> def foo(): ... x = 1 >>> foo() >>> print x # 1 Traceback (most recent call last): ... NameError: name 'x' is not defined |