Python 装饰器的总结

先来了解几个定义:

1,函数

在python中,函数通过def关键字、函数名和可选的参数列表定义。通过return关键字返回值。我们举例来说明如何定义和调用一个简单的函数:

?


1

2

3

4

5

6

7

#coding:UTF8

def foo():

     return 1

print foo()

1

方法体(当然多行也是一样的)是必须的,通过缩进来表示,在方法名的后面加上双括号()就能够调用函数

2,作用域

在Python中,函数会创建一个新的作用域. Python开发者可能会说函数有自己的命名空间.这就意味着在函数内部碰到一个变量的时候函数会优先在自己的命名空间里寻找.来简单举例说明本地作用域与全局作用域

?


1

2

3

4

5

6

7

8

9

10

11

12

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

{}

内置的函数globals返回一个包含所有Python解释器知道的变量名称的字段(省略一部分)在#2调用了函数foo 把函数背部本地作用域里面的内容打印出来.可以看到,函数foo有自己的独立的命名空间,即使暂时命名空间啥也没有.

3,变量解析规则

当然并不是说在函数里就不能访问外面的全局变量.在Python的作用域规则里,创建变量一定会在当前作用域里创建一个变量,但访问或者修改变量是会现在当前作用域查找变量,没有找到匹配变量会依次向上在闭合的作用域里进行查找.so 如修改函数foo的是实现打印全局的作用域的变量也是可以的

?


1

2

3

4

5

6

7

8

9

#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,当然在函数的本地作用域是找不到,so接着会在上层的作用域去查找
但在另外一方面,假如在函数的内部给全局变量赋值,结果会不一样 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

#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

全局变量能够被访问到(如果是可变数据类型(像list,dict这些),甚至能够被更改),但赋值就不行了,在函数内部的#1,实际上新创建了一个局部变量,隐藏全局作用域中的同名变量,可以通过打印出全局命名空间中的内容得出这个结论.也可以在#2处打印出来的a_string没有改变

4,变量生存周期

值得注意的是:变量不仅是生存在一个个的命名空间里,都有自己的生存周期,如下:

?


1

2

3

4

5

6

7

8

#coding:UTF8

def foo():

     x = 1

foo()

print x # 1

NameError: name ‘x‘ is not defined

#1处发生的错误不仅仅是因为作用域规则导致,而且和Python以及其他很多编程语言中函数调用实现的机制有关.在此地方执行时间点并没有什么有效的语法可以获取变量x的值,因为压根就不存在,函数foo的命名空间随着函数的调用开始而开始,结束而销毁

5,函数参数

Python允许想函数传递参数,参数会变成本地变量存在与函数内部

?


1

2

3

4

5

6

#coding:UTF8

def foo(x):

     print locals()

foo(1)

{‘x‘: 1}

在Python中有很多的方式来定义和传递参数,简要说明下:函数的参数是必须的位置参数或是可选的命名,默认参数

?


1

2

3

4

5

6

7

8

9

10

11

12

#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允许创建嵌套函数,就意味着可以在函数里定义函数而且现有的作用域和变量生存周期依旧适用

?


1

2

3

4

5

6

7

8

9

10

11

#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里函数和其他东西一样都是对象

?


1

2

3

4

5

6

7

8

9

#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里,函数只是一些普通的值而已,也就是说把函数像参数一样传递给其他的函数或者从函数里返回函数,如:

?


1

2

3

4

5

6

7

8

9

10

11

#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参数 从而 来自定义排序规则

?


1

2

3

4

5

6

7

8

9

10

11

12

#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,闭包

?


1

2

3

4

5

6

7

8

9

10

11

#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会是同样的逻辑
稍微改动下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

#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,装饰器

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

#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就行了:

?


1

foo = outer(foo)

现在,任何怎么调用都不会牵扯到原先的函数foo,都会得到新的装饰版本的foo,现在还是来写一个有用的装饰器

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#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

  

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

Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。在上一节的例子里我们是将原本的方法用装饰后的方法代替:

?


1

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 deco
11 @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 test1
25 the func run time  is 1.05200004578
26 (‘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 home
15                     print("---after authenticaion ")
16                     return res
17                 else:
18                     exit("\033[31;1mInvalid username or password\033[0m")
19             elif auth_type == "ldap":
20                 print("搞毛线ldap,不会。。。。")
21
22         return wrapper
23     return outer_wrapper
24
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()
时间: 2024-10-05 20:24:15

Python 装饰器的总结的相关文章

5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器

一.什么是装饰器? 实际上装饰器就是个函数,这个函数可以为其他函数提供附加的功能. 装饰器在给其他函数添加功能时,不会修改原函数的源代码,不会修改原函数的调用方式. 高阶函数+函数嵌套+闭包 = 装饰器 1.1什么是高阶函数? 1.1.1函数接收的参数,包涵一个函数名. 1.1.2 函数的返回值是一个函数名. 其实这两个条件都很好满足,下面就是一个高阶函数的例子. def test1(): print "hamasaki ayumi" def test2(func): return t

python装饰器通俗易懂的解释!

python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说明一下: 小P闲来无事,随便翻看自己以前写的一些函数,忽然对一个最最最基础的函数起了兴趣: 1 def sum1(): 2 sum = 1 + 2 3 print(sum) 4 sum1() 此时小P想看看这个函数执行用了多长时间,所以写了几句代码插进去了: 1 import time 2 3 def

python装饰器1

第八步:让装饰器带 类 参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # -*- coding:gbk -*- '''示例8: 装饰器带类参数''' class locker:     def __init__(self):         print("locker.__init__() should be not called.")   

Python装饰器由浅入深

装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们以装饰函数为例子介绍其用法.要理解在Python中装饰器的原理,需要一步一步来.本文尽量描述得浅显易懂,从最基础的内容讲起. (注:以下使用Python3.5.1环境) 一.Python的函数相关基础 第一,必须强调的是python是从上往下顺序执行的,而且碰到函数的定义代码块是不会立即执行它的,只

python 装饰器学习(decorator)

最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initial decorator" f() def __call__(self): print "call decorator" @decorator def fun(): print "in the fun" print "after " fun

【转】九步学习python装饰器

本篇日志来自:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 纯转,只字未改.只是为了学习一下装饰器.其实现在也是没有太看明白,对于装饰器我就是用的时候找例子,能蒙对,但是用过之后一段时间就忘了.还是用的少.有空应该好好看一看的,包括闭包.对于各种现代编程语言来说闭包都是很重要的.在这里先谢过原作者,如有侵权请告知. =-=-=-=-=-=-=-=-=-=-一条不怎么华丽的分隔线-=-=-=-=-=-=-=-=-=-= 这

【Python之旅】第四篇(一):Python装饰器

有时候拿到一个程序接口,需要对其进行扩展,但是又不能修改原来接口的源代码,这时候就需要使用装饰器了. 有下面一个小程序,假如是别人提供给我们的调用接口: import time def sayHi():         time.sleep(1)         print 'Hello, I am xpleaf.' 一般情况下,如果想要计算该程序的执行时间(因为有可能要对该接口进行某些性能上的测试),就需要把以上接口修改为下面这样,同时执行一下: 程序代码: import time def s

python装饰器原理及相关操作

python装饰器,简单的说就是用于操作底层代码的代码,在不改变底层代码函数的情况下对底层代码进行验证操作等 首先,必须知,道调用func和func的区别,分别为返回函数所在的内存地址和调用该函数,输出执行结果,例如: def func(): print("欢迎光临!!!") print("返回函数所在的内存地址:",func) func() 列举一个简单的web页面调用例子 1 #做登录验证 2 def login(func): 3 print("登录成

python装饰器学习笔记

什么是python装饰器? 装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问. eg:当需要在Func1和Func2中加一样的功能时,可以在outer中添加一次就可以完成全部函数的添加.装饰器与函数建立连接的方式是在函数的前一行用@+装饰器名称来完成.并且在装饰器中一定要返回被装饰的对象 def outer(fun):     def wrapper():         print '验证'         

python 装饰器及标准库functools中的wraps

最近在看 flask的视图装饰器 时,忽然想起预(复)习一下python的装饰器. 这里有一篇比较好的讲解装饰器的书写的 Python装饰器学习(九步入门) . 这里不单独记录装饰器的书写格式了,重点是工作流程. 首先常见的 装饰器 格式就是通过@语法糖,简便的写法,让流程有些不太清楚. 装饰器不带参数的情况下: def deco(func):     def _deco():         print("before myfunc() called.")         func(