Home  >  Article  >  Backend Development  >  Detailed introduction to Python decorators

Detailed introduction to Python decorators

零下一度
零下一度Original
2017-07-20 21:16:121204browse

The decorator itself is a Python function. It allows other functions to add additional functions without making any code changes. The return value of the decorator Also an additional object.

Let’s first understand a few definitions:

1, function

In python, functions are defined through the def keyword, function name and optional parameter list. Return a value through the return keyword. Let’s take an example to illustrate how to define and call a simple function:

#coding:UTF8

def foo():
     return 1
print foo()

1

The method body (of course the same applies to multiple lines) is required , expressed by indentation. Add double brackets () after the method name to call the function

2, scope

In Python, functions create a new scope. Python developers may say that functions have their own namespace. This means that when a variable is encountered inside the function, the function will first look for it in its own namespace. Let’s give a simple example of local scope and global scope

#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;,...}
{}

The built-in function globals returns a list containing all The field of the variable name known to the Python interpreter (omitting part of it) calls the function foo in #2 to print out the contents of the local scope at the back of the function. As you can see, the function foo has its own independent namespace, even if it is a temporary namespace Nothing.

3, variable parsing rules

Of course it does not mean that external global variables cannot be accessed in a function. In Python In the scope rules of , creating a variable will definitely create a variable in the current scope, but accessing or modifying a variable will search for the variable in the current scope. If no matching variable is found, the search will be performed upwards in the closed scope. so If you modify function foo to print global scope variables, it is also possible

#coding:UTF8

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

foo()

This is a global variable

At #1, the Python interpreter will try to find the variable a_string, Of course, it cannot be found in the local scope of the function, so it will then search for
in the upper scope. But on the other hand, if a global variable is assigned a value inside the function, the result will be different. Same as

#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

Global variables can be accessed (if they are variable data types (like list, dict These), can even be changed), but assignment does not work. #1 inside the function actually creates a new local variable, hiding the variable with the same name in the global scope, and you can print out the contents of the global namespace Draw this conclusion. It can also be seen that the a_string printed at #2 has not changed

4, the variable life cycle

It is worth noting that : Variables not only live in namespaces, but also have their own life cycle, as follows:

#coding:UTF8

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

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

The error that occurs at #1 is not only caused by scoping rules, but also related to the mechanism of function call implementation in Python and many other programming languages. There is no valid syntax to obtain the variable x at this execution time point. The value, because it does not exist at all, the namespace of function foo starts with the call of the function and is destroyed when it ends

5, function parameters

Python allows passing parameters to functions, and the parameters will become local variables and exist inside the function

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

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

There are many ways to define and Pass parameters, a brief explanation: the parameters of the function are required positional parameters or optional naming, the default parameters are

#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()

 

The above is the detailed content of Detailed introduction to Python decorators. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn