Python 装饰器的总结
先来了解几个定义:
1,函数
在python中,函数通过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 {'foo': <function foo at 0x00000000026ECF98>, ...., 'a_string': 'This is a global variable',...}
{}
内置的函数globals返回一个包含所有Python解释器知道的变量名称的字段(省略一部分)在#2调用了函数foo 把函数背部本地作用域里面的内容打印出来.可以看到,函数foo有自己的独立的命名空间,即使暂时命名空间啥也没有.
3,变量解析规则
当然并不是说在函数里就不能访问外面的全局变量.在Python的作用域规则里,创建变量一定会在当前作用域里创建一个变量,但访问或者修改变量是会现在当前作用域查找变量,没有找到匹配变量会依次向上在闭合的作用域里进行查找.so 如修改函数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,当然在函数的本地作用域是找不到,so接着会在上层的作用域去查找
但在另外一方面,假如在函数的内部给全局变量赋值,结果会不一样
#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,变量生存周期
值得注意的是:变量不仅是生存在一个个的命名空间里,都有自己的生存周期,如下:
#coding:UTF8 def foo():
x = 1
foo()
print x # 1 NameError: name 'x' is not defined
#1处发生的错误不仅仅是因为作用域规则导致,而且和Python以及其他很多编程语言中函数调用实现的机制有关.在此地方执行时间点并没有什么有效的语法可以获取变量x的值,因为压根就不存在,函数foo的命名空间随着函数的调用开始而开始,结束而销毁
5,函数参数
Python允许想函数传递参数,参数会变成本地变量存在与函数内部
#coding:UTF8
def foo(x):
print locals()
foo(1) {'x': 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 '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,而且能够对它进行调用
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('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支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。在上一节的例子里我们是将原本的方法用装饰后的方法代替:
bar=test2(bar)
这种方式能够在任何时候对任意方法进行包装。但是如果自定义一个方法,可以使用@进行装饰:
#coding:UTF8 import time def test2(func):
print(func)
return func
@test2
def bar():
time.sleep(2)
print('in the bar') bar() #run bar
#coding:UTF8 import time
def timer(func): #timer(test1) func=test1
def deco(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs) #run test1()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
return deco
@timer #test1=timer(test1)
def test1():
time.sleep(1)
print('in the test1') @timer # test2 = timer(test2) = deco test2(name) =deco(name)
def test2(name,age):
print("test2:",name,age) test1()
test2("Tom",22) in the test1
the func run time is 1.05200004578
('test2:', 'Tom', 22)
the func run time is 0.0
下面贡献一个高级版的装饰器:
#coding:utf8
import time
user,passwd = 'hbert','abc'
def auth(auth_type):
print("auth func:",auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
#print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = raw_input("Username:").strip()
password = raw_input("Password:").strip()
if user == username and passwd == password:
print("\033[32;1mUser has passed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authenticaion ")
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("搞毛线ldap,不会。。。。") return wrapper
return outer_wrapper def index():
print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home" @auth(auth_type="ldap")
def bbs():
print("welcome to bbs page") index()
print(home()) #wrapper()
bbs()
Python 装饰器的总结的更多相关文章
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
- python装饰器通俗易懂的解释!
1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...
- Python 装饰器学习
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...
- python 装饰器修改调整函数参数
简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...
- python 装饰器学习(decorator)
最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...
- Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...
- 关于python装饰器(Decorators)最底层理解的一句话
一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...
- Python装饰器由浅入深
装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...
- Python装饰器与面向切面编程
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
- python装饰器方法
前几天向几位新同事介绍项目,被问起了@login_required的实现,我说这是django框架提供的装饰器方法,验证用户是否登录,只要这样用就行了,因为自己不熟,并没有做过多解释. 今天查看dja ...
随机推荐
- WPF通过<x:Array>直接为ListBox的ItemsSource赋值
<!--其中sys前缀是在xmlns中引入了System的命名空间--> <ListBox.ItemsSource> <x:Array Type="{x:Typ ...
- WPF简单MVVM实现
1. MVVM介绍: MVVM就是: Model -- 模型(现实中对象的抽象) View -- UI(用户界面) ViewModel -- UI界面的抽象(给View提供数据,并响应View的操作) ...
- 如何处理服务器SSL收到了一个弱临时Diffie-Hellman 密钥?
当我们用火狐浏览器打开某个HTTPS网站时可能会失败,并且出现如下错误提示: 安全连接失败连接某个URL网址时发生错误. 在服务器密钥交换握手信息中 SSL 收到了一个弱临时 Diff ...
- 搭建nginx代理,为前端页面跨域调用接口
前端同学因开发需要,本地搭建的服务需要调用其它域名的接口,在帮助正确配置后,已能正常使用. 这里写一篇博客,记录一下. 前端页面地址为127.0.0.1:9813/a.html 接口地址http:// ...
- 第9章 scrapy-redis分布式爬虫
9-1 分布式爬虫要点 1.分布式的优点 充分利用多机器的宽带加速爬取 充分利用多机的IP加速爬取速度 问:为什么scrapy不支持分布式? 答:在scrapy中scheduler是运行在队列的,而队 ...
- flask中的数据操作
flask中数据访问: pip install flask-sqlalemy 创建数据: 创建app的工厂 from flask import Flask from flask_sqlalchemy ...
- 四、curator recipes之共享重入互斥锁
简介 curator的recipes实现了可重入互斥锁,允许你在分布式场景下多个进程之间实现锁的互斥以协调多进程执行. 相关类:InterProcessMutex 官方文档:http://curato ...
- SparkGraphx计算指定节点的N度关系节点
直接上代码: package horizon.graphx.util import java.security.InvalidParameterException import horizon.gra ...
- bnu 被诅咒的代码
http://www.bnuoj.com/bnuoj/problem_show.php?pid=10792 被诅咒的代码 Time Limit: 1000ms Memory Limit: 65536K ...
- Spring入门(三)— AOP注解、jdbc模板、事务
一.AOP注解开发 导入jar包 aop联盟包. aspectJ实现包 . spring-aop-xxx.jar . spring-aspect-xxx.jar 导入约束 aop约束 托管扩展类和被扩 ...