デコレーター自体は Python 関数であり、コードを変更せずに他の関数が追加のオブジェクトを追加できるようになります。
まず、いくつかの定義を理解しましょう:
Python では、関数は def キーワード、関数名、およびオプションのパラメーター リストを通じて定義されます。 return キーワードを使用して値を返します。単純な関数を定義して呼び出す方法を説明するための例を見てみましょう:
#coding:UTF8 def foo(): return 1 print foo() 1
メソッド本体 (もちろん、複数行でも同じです) は必須であり、インデントで表現され、二重括弧が追加されます。メソッド名 () の後に関数
を呼び出すことができます。Python では、関数が独自の名前空間を作成するということを意味します。変数が内部で検出されると、関数はまずローカル スコープとグローバル スコープの簡単な例を示します。組み込み関数 globals は、すべてを含むリストを返します。 Pythonの説明 プロセッサが認識している変数名のフィールド(一部省略) #2の関数fooを呼び出して、関数の後ろのローカルスコープの内容を出力していることがわかります。一時名前空間が No でない場合でも、独自の独立した名前空間を使用します。
3、変数解析ルール もちろん、Python のスコープ ルールでは、外部グローバル変数にアクセスできないという意味ではありません。変数を作成すると、確実に現在のスコープ内に変数が作成されますが、変数にアクセスまたは変更する場合、変数は現在のスコープ内で検索され、一致する変数が見つからない場合、検索は閉じられたスコープ内で上向きに実行されます。関数 foo がグローバル スコープの変数を出力するように変更されている場合、それも可能です
#coding:UTF8 a_string = "This is a global variable" def foo(): print locals() print globals() # doctest: +ELLIPSIS foo() #2 {'foo': <function foo at 0x00000000026ECF98>, ...., 'a_string': 'This is a global variable',...} {}
しかし、一方で、グローバル変数に関数内で値が割り当てられている場合、結果は異なります
#coding:UTF8 a_string = "This is a global variable" def foo(): print a_string #1 foo() This is a global variable
グローバル変数にはアクセスできますが (変数データ型 (リスト、辞書など) の場合は、変更することもできます)、関数内の代入 #1 は実際には機能しません。新しいローカル変数は、グローバル スコープ内で同じ名前の変数を隠します。この結論は、グローバル名前空間の内容を出力することによって導き出すことができます。また、#2 で出力できる a_string も変更されていません
#coding:UTF8 a_string = "This is a global variable" def foo(): a_string='Test' #1 print locals() foo() {'a_string': 'Test'} print a_string #2 This is a global variable
#1 で発生したエラーは、これはスコープ ルールによってのみ発生しますが、Python や他の多くのプログラミング言語での関数呼び出し実装のメカニズムにも関連しています。 ここには実行時間はありません。 変数の値を取得するための効果的な構文は何ですか。 パラメータを渡すと、パラメータは次のようになります。ローカル変数であり、関数内に存在します
#coding:UTF8 def foo(): x = 1 foo() print x # 1 NameError: name 'x' is not defined
#coding:UTF8 def foo(x): print locals() foo(1) {'x': 1}
在#1处定义了函数foo,有一个位置参数x和一个命名参数y 在#2通过常规的方式来调用函数,即使只有一个命名参数,但参数依然可以通过位置参数传递给函数.在调用函数的时候,对于命名参数y也可以完全不管就想#3所示一样.如命名参数没有接收到任何值的话,Python会自动使用声明的默认值.但不能省略第一个位置参数x,否则会像#4发生错误
python支持函数调用时的命名参数。看看#5处的函数调用,传递的是两个命名实参,这个时候因为有名称标识,参数传递的顺序也就不用在意了。
当然相反的情况也是正确的:函数的第二个形参是y,但通过位置的方式传递值给它。在#2处的函数调用foo(3,1),我们把3传递给了第一个参数,把1传递给了第二个参数,尽管第二个参数是一个命名参数。
Python允许创建嵌套函数,就意味着可以在函数里定义函数而且现有的作用域和变量生存周期依旧适用
#coding:UTF8 def outer(): x = 1 def inner(): print x # 1 inner() # 2 outer() 1
Python解释器需找一个叫x的本地变量,查找失败之后会继续向上层的作用域里查,这个上层的作用域定义在另外一个函数里,对于函数outer来说,变量x是一个本地变量
函数inner可以访问封闭的作用域.在#2处,可以调用函数inner,inner也仅仅是一个遵循Python变量解析规则的变量名,Python解释器会优先在outer的作用域里面对变量名inner查找匹配的变量
在Python里函数和其他东西一样都是对象
#coding:UTF8 print issubclass(int, object) # all objects in Python inherit from a common baseclass True def foo(): pass print foo.__class__ # 1 <type 'function'> print issubclass(foo.__class__, object) True
函数在Python里就是对象,和其他一样,在Python里,函数只是一些普通的值而已,也就是说把函数像参数一样传递给其他的函数或者从函数里返回函数,如:
#coding:UTF8 def add(x, y): return x + y def sub(x, y): return x - y def apply(func, x, y): # 1 return func(x, y) # 2 print apply(add, 2, 1) # 3 3 print apply(sub, 2, 1) 1
在#1处看到函数准备接收一个函数的变量,只是一个普通的变量而已,和其他变量一样,在#2处调用传进来的函数:"()代表这调用函数的操作并且调用变量包含额值.在#3处,能看到传递函数并没有特殊的用法".函数的名称只是跟其他变量一样的标识符而已
Python把频繁要用的操作变成函数作为参数进行使用,向通过传递一个函数给内置排序函数的key参数 从而 来自定义排序规则
#coding:UTF8 def outer(): def inner(): print "Inside inner" return inner # 1 foo = outer() #2 print foo <function inner at 0x000000000269C048> foo() Inside inner
在#1处恰好是函数标识符的变量inner作为返回值返回出来 "把函数inner返回出来,否则它根本不可能会被调用到" 每次函数outer呗调用,函数inner都会被重新定义,如果它不被当做变量返回额话,每次执行过后将不复存在
在#2处捕获返回值--函数inner,将它存在一个新的变量foo里.当对foo进行求值,确定包含函数inner,而且能够对它进行调用
#coding:UTF8 def outer(): x = 1 def inner(): print x # 1 return inner foo = outer() print foo.func_closure (<cell at 0x00000000026861F8: int object at 0x0000000001E279A8>,)
x是outer里的一个局部变量,当函数inner在#1处打印x时,Python解释器会在inner内部查找相应的变量,事实也查不到,接着会到封闭作用域里查找,并且找到匹配
从变量的生存周期来看,变量x是函数outer的一个本地变量,意味着只有当函数outer正在运行时才会存在,根据Python运行模式,无法再函数outer返回之后继续调用函数inner,在函数inner调用时,变量x早已不复存在,可能会发生一个运行时的错误
但返回的函数inner可以继续工作,Python支持一个叫做函数闭包的特性,嵌套定义在非全局作用域里的函数能够记住它在被定义的时候它所处的封闭命名空间,这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如x,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)
每次函数outer被调用的时候,函数inner都会被重新定义。现在变量x的值不会变化,所以每次返回的函数inner会是同样的逻辑
稍微改动下:
#coding:UTF8 def outer(x): def inner(): print x # 1 return inner print1 = outer(1) print2 = outer(2) print1() 1 print2() 2
从中可以看到闭包--被函数记住的封闭作用域--能够被用来创建自定义的函数,本质上是一个硬编码的参数.事实上并不是传递参数1或者2给函数inner,实际上是创建了能够打印各种数字的各种自定义版本
闭包单独拿出来就是一个非常强大的功能,在某些方面:outer像是给inner服务器的构造器,x像是一个私有变量
装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版参数
#coding:UTF8 def outer(func): def inner(): print "before func" ret = func() # 1 return ret + 1 return inner def foo(): return 1 decorated = outer(foo) # 2 print decorated() before func 2
定义了一个函数outer,只有一个func参数,在其定义了嵌套的函数inner,inner会打印一串字符串,然后调用func,在#1得到返回值,在outer每次调用时func值可能会不一样,但不管怎用,都会调用它,最后,inner返回func()+1的值,通过调用在#2处存储decorated里的函数能够看到被打印出来的字符串以及返回值2,而不是期望中调用函数foo得到的返回值1。
可以认为变量decorated是函数foo的一个装饰版本,一个加强版本。事实上如果打算写一个有用的装饰器的话,可能会想愿意用装饰版本完全取代原先的函数foo,这样总是会得到我们的”加强版“foo。想要达到这个效果,完全不需要学习新的语法,简单地赋值给变量foo就行了:
foo = outer(foo)
现在,任何怎么调用都不会牵扯到原先的函数foo,都会得到新的装饰版本的foo,现在还是来写一个有用的装饰器
#coding:UTF8 import time def bar(): time.sleep(2) print('in the bar') def test2(func): print(func) return func # print(test2(bar)) bar=test2(bar) bar() #run bar <function bar at 0x00000000026BCF98> in the bar
Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。在上一节的例子里我们是将原本的方法用装饰后的方法代替:
bar=test2(bar)
这种方式能够在任何时候对任意方法进行包装。但是如果自定义一个方法,可以使用@进行装饰:
1 #coding:UTF8 2 3 import time 4 5 def test2(func): 6 print(func) 7 return func 8 @test2 9 def bar():10 time.sleep(2)11 print('in the bar')12 13 bar() #run bar
1 #coding:UTF8 2 3 import time 4 def timer(func): #timer(test1) func=test1 5 def deco(*args,**kwargs): 6 start_time=time.time() 7 func(*args,**kwargs) #run test1() 8 stop_time = time.time() 9 print("the func run time is %s" %(stop_time-start_time))10 return deco11 @timer #test1=timer(test1)12 def test1():13 time.sleep(1)14 print('in the test1')15 16 @timer # test2 = timer(test2) = deco test2(name) =deco(name)17 def test2(name,age):18 print("test2:",name,age)19 20 test1()21 test2("Tom",22)22 23 24 in the test125 the func run time is 1.0520000457826 ('test2:', 'Tom', 22)27 the func run time is 0.0
下面贡献一个高级版的装饰器:
1 #coding:utf8 2 import time 3 user,passwd = 'hbert','abc' 4 def auth(auth_type): 5 print("auth func:",auth_type) 6 def outer_wrapper(func): 7 def wrapper(*args, **kwargs): 8 #print("wrapper func args:", *args, **kwargs) 9 if auth_type == "local":10 username = raw_input("Username:").strip()11 password = raw_input("Password:").strip()12 if user == username and passwd == password:13 print("\033[32;1mUser has passed authentication\033[0m")14 res = func(*args, **kwargs) # from home15 print("---after authenticaion ")16 return res17 else:18 exit("\033[31;1mInvalid username or password\033[0m")19 elif auth_type == "ldap":20 print("搞毛线ldap,不会。。。。")21 22 return wrapper23 return outer_wrapper24 25 def index():26 print("welcome to index page")27 @auth(auth_type="local") # home = wrapper()28 def home():29 print("welcome to home page")30 return "from home"31 32 @auth(auth_type="ldap")33 def bbs():34 print("welcome to bbs page")35 36 index()37 print(home()) #wrapper()38 bbs()
以上がPython デコレータの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。