Heim > Artikel > Backend-Entwicklung > Detaillierte Einführung in Python-Dekoratoren
Der Dekorator selbst ist eine Python-Funktion, die es anderen Funktionen ermöglicht, zusätzliche Funktionen hinzuzufügen, ohne Codeänderungen vorzunehmen. Der Rückgabewert des Dekorators ist ebenfalls ein zusätzliches Objekt .
Lassen Sie uns zunächst einige Definitionen verstehen:
In Python werden Funktionen über das Schlüsselwort def, den Funktionsnamen und die optionale Parameterliste definiert. Geben Sie einen Wert über das Schlüsselwort return zurück. Nehmen wir ein Beispiel, um zu veranschaulichen, wie eine einfache Funktion definiert und aufgerufen wird:
#coding:UTF8 def foo(): return 1 print foo() 1
Methodenkörper (dasselbe gilt natürlich auch auf mehrere Zeilen) Es ist notwendig, durch Einrückung ausgedrückt, und die Funktion kann durch Hinzufügen doppelter Klammern () nach dem Methodennamen
In Python erstellt eine Funktion einen neuen Bereich. Python-Entwickler sagen möglicherweise, dass Funktionen ihren eigenen Namensraum haben. Das bedeutet, dass sich die Funktion zunächst in ihrem eigenen Namensraum befindet. Um ein einfaches Beispiel für den lokalen und globalen Bereich zu geben
#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',...} {}
Eingebaute Funktion globals Gibt zurück Ein Feld, das alle dem Python-Interpreter bekannten Variablennamen enthält (wobei ein Teil davon weggelassen wird). In #2 wird die Funktion foo aufgerufen, um den Inhalt des lokalen Bereichs am Ende der Funktion auszugeben foo verfügt über einen eigenen unabhängigen Namespace. Auch wenn sich im temporären Namespace nichts befindet.
Natürlich das bedeutet nicht, dass Sie in einer Funktion nicht auf die Außenwelt zugreifen können. In den Bereichsregeln von Python wird beim Erstellen einer Variablen definitiv eine Variable im aktuellen Bereich erstellt, beim Zugriff oder Ändern einer Variablen wird jedoch nach der Variablen im aktuellen Bereich gesucht. Wenn keine passende Variable gefunden wird, wird sie im geschlossenen Bereich Search.so nach oben verschoben. Wenn Sie die Funktion foo so ändern, dass sie globale Bereichsvariablen ausgibt, ist dies auch möglich
#coding:UTF8 a_string = "This is a global variable" def foo(): print a_string #1 foo() This is a global variable
bei #1 versucht der Python-Interpreter, die Variable a_string zu finden. Natürlich kann sie nicht im lokalen Bereich der Funktion gefunden werden, daher wird nach
in gesucht Wenn der globale Gültigkeitsbereich jedoch innerhalb der Funktionsvariablenzuweisung angegeben wird, sind die Ergebnisse unterschiedlich
#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
Auf globale Variablen kann zugegriffen werden (sofern auf sie zugegriffen werden kann). Der Datentyp (wie Liste, Diktat usw.) kann sogar geändert werden, aber die Zuweisung Nr. 1 innerhalb der Funktion funktioniert nicht Erstellt tatsächlich eine neue lokale Variable und versteckt die Variable mit demselben Namen im globalen Bereich. Sie können den Inhalt des globalen Namespace ausdrucken, um diese Schlussfolgerung zu ziehen. Sie können auch sehen, dass sich der bei #2 gedruckte a_string nicht geändert hat 🎜>
4, Variablenlebensdauer
#coding:UTF8 def foo(): x = 1 foo() print x # 1 NameError: name 'x' is not defined
Der Fehler, der bei #1 auftritt, wird nicht nur durch Bereichsregeln verursacht, sondern auch Bezieht sich auf den Mechanismus der Funktionsaufrufimplementierung in Python und vielen anderen Programmiersprachen. Der Ausführungszeitpunkt hier ist keine gültige Syntax, um den Wert der Variablen x zu erhalten, da er überhaupt nicht existiert. Der Namespace der Funktion foo beginnt mit Aufruf der Funktion und wird zerstört, wenn er beendet wird
5, Funktionsparameter
#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()
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Python-Dekoratoren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!