>백엔드 개발 >파이썬 튜토리얼 >Python 데코레이터에 대한 자세한 소개

Python 데코레이터에 대한 자세한 소개

零下一度
零下一度원래의
2017-07-20 21:16:121214검색

데코레이터 자체는 Python 함수입니다. 데코레이터의 반환 값도 추가 개체입니다.

먼저 몇 가지 정의를 이해해 보겠습니다.

1, 함수

파이썬에서 함수는 def 키워드, 함수 이름, 선택적 매개변수 목록을 통해 정의됩니다. return 키워드를 통해 값을 반환합니다. 간단한 함수를 정의하고 호출하는 방법을 설명하기 위해 예를 들어보겠습니다.

#coding:UTF8

def foo():
     return 1
print foo()

1

메서드 본문(물론 여러 줄의 경우에도 동일)이 필요하며 들여쓰기로 표현되며 이중 괄호가 추가됩니다. 메서드 이름() 뒤에는

2, 범위

함수를 호출할 수 있습니다. Python에서 함수는 새 범위를 생성합니다. Python 개발자는 함수에 자체 네임스페이스가 있음을 의미합니다. 내부적으로 변수가 발견되면 함수는 먼저 자체 네임스페이스에서 검색합니다. 로컬 범위와 전역 범위의 간단한 예를 들어 보겠습니다.

#coding:UTF8

a_string = "This is a global variable"
def foo():
     print locals()
print globals() # doctest: +ELLIPSIS

foo()  #2


{&#39;foo&#39;: <function foo at 0x00000000026ECF98>, ...., &#39;a_string&#39;: &#39;This is a global variable&#39;,...}
{}

내장 함수 globals는 모든 항목을 포함하는 목록을 반환합니다. Python 설명 프로세서에 알려진 변수 이름 필드(일부 생략)는 #2의 foo 함수를 호출하여 함수 뒷부분의 로컬 범위에 있는 내용을 인쇄합니다. 임시 네임스페이스가 아니더라도 자체의 독립적인 네임스페이스입니다.

3, 변수 구문 분석 규칙

물론 함수에서 외부 전역 변수에 접근할 수 없다는 의미는 아닙니다. 변수를 생성하면 당연히 현재 범위에서 변수가 생성되지만, 변수에 액세스하거나 수정하면 해당 변수가 현재 범위에서 검색됩니다. 따라서 일치하는 변수가 없으면 닫힌 범위에서 위쪽으로 검색이 수행됩니다. foo 함수를 수정하여 전역 범위의 변수를 인쇄하는 것도 가능합니다

#coding:UTF8

a_string = "This is a global variable"
def foo():
     print a_string   #1

foo()

This is a global variable

#1에서 Python 인터프리터는 변수 a_string을 찾으려고 시도합니다. 물론 변수는 a_string에서 찾을 수 없습니다. 함수의 로컬 범위이므로 상위 범위에서 찾습니다
하지만 반면에 전역 변수에 함수 내부의 값이 할당되면 결과가 달라집니다

#coding:UTF8

a_string = "This is a global variable"
def foo():
    a_string=&#39;Test&#39;  #1
    print locals()
    
foo()

{&#39;a_string&#39;: &#39;Test&#39;}

print a_string  #2

This is a global variable

전역 변수에 액세스할 수 있지만(변수 데이터 유형(예: 목록, 딕셔너리 등)인 경우에도 액세스할 수 있습니다. 변경) #1 함수 내부에서는 할당이 작동하지 않습니다. 전역 범위에서 동일한 이름을 가진 변수를 숨기는 새 지역 변수. 전역 네임스페이스의 내용을 인쇄하면 이러한 결론을 내릴 수 있습니다. 또한 #2에서 인쇄할 수 있는 a_string은 변경되지 않았습니다

4. 변수 수명 주기

변수는 네임스페이스에 존재할 뿐만 아니라 다음과 같이 고유한 수명 주기를 갖는다는 점에 주목할 가치가 있습니다.

#coding:UTF8

def foo():
     x = 1
foo()
print x # 1

NameError: name &#39;x&#39; is not defined

#1에서 발생한 오류는 범위 규칙에 의해서만 발생하지만 Python 및 기타 여러 프로그래밍 언어의 함수 호출 구현 메커니즘과도 관련이 있습니다. 여기에는 실행 시간이 없습니다. 변수 Pass 매개변수의 값을 가져오는 효과적인 구문은 무엇이며 매개변수는 다음과 같습니다. 지역 변수는 함수 내부에 존재합니다

#coding:UTF8
def foo(x):
     print locals()
foo(1)

{&#39;x&#39;: 1}

Python에서 매개변수를 정의하고 전달하는 방법에는 여러 가지가 있습니다. 간략한 설명: 함수의 매개변수는 필수 위치 매개변수 또는 선택적 이름 지정, 기본 매개변수

#coding:UTF8
def foo(x, y=0): # 1
     return x - y
 
print foo(3, 1) # 2
2
print foo(3) # 3
3
print foo() # 4
TypeError: foo() takes at least 1 argument (0 given)
print foo(y=1, x=3) # 5
2

입니다.

  

  在#1处定义了函数foo,有一个位置参数x和一个命名参数y 在#2通过常规的方式来调用函数,即使只有一个命名参数,但参数依然可以通过位置参数传递给函数.在调用函数的时候,对于命名参数y也可以完全不管就想#3所示一样.如命名参数没有接收到任何值的话,Python会自动使用声明的默认值.但不能省略第一个位置参数x,否则会像#4发生错误

python支持函数调用时的命名参数。看看#5处的函数调用,传递的是两个命名实参,这个时候因为有名称标识,参数传递的顺序也就不用在意了。

当然相反的情况也是正确的:函数的第二个形参是y,但通过位置的方式传递值给它。在#2处的函数调用foo(3,1),我们把3传递给了第一个参数,把1传递给了第二个参数,尽管第二个参数是一个命名参数。

6,嵌套函数

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查找匹配的变量

7,函数是Python世界中的一级类对象

在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 &#39;function&#39;>
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,而且能够对它进行调用

8,闭包

#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像是一个私有变量

9,装饰器

装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版参数

#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(&#39;in the bar&#39;)
def test2(func):
    print(func)
    return func

# print(test2(bar))
bar=test2(bar)
bar()  #run bar

<function bar at 0x00000000026BCF98>
in the bar

  

10. 使用 @ 标识符将装饰器应用到函数和利用*args and **kwargs

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.