Python(装饰器)
eval 内容从字符串中提取出来,用Python运行一遍
__name__得到函数名字
|
闭包函数 |
定义: |
闭包其实就是不管在哪里调用,都自带变量,不会有调用全局变量,而全局变量更改过了的错误产生 |
|
特点: |
f.__closure__[1].cell_contents |
|
|
另:通常需要把自己return出来,使自己能够在外部使用 |
|
开放、封闭原则: |
对扩展是开放的,对修改是封闭的 |
|||
|
装饰器 |
装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用对象 |
功能 |
在不修改装饰器对象源代码,以及调用方式的前提下,为其添加新功能 |
1、不修改源代码 |
|
语法 |
在被装饰对象的正上方写上 @[装饰器名字], |

添加被装饰函数的原生备注(在装饰器上添加@wraps)如下:
import time
import random
from functools import wraps def timmer(func):
@wraps(func)
def wrapper():
# wrapper.__doc__=func.__doc__
start_time = time.time()
func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time)) # wrapper.__doc__=func.__doc__
return wrapper @timmer
def index():
'index function'
time.sleep(3)
print('welecome to index page') print(index.__doc__)
#调用被装饰函数的注释
#============================================================================================
# def zs(func): #这里出过错,没有定义形参,使得函数内部调用了yuan(),但是yuan()是后面定义的
# def zs1():
# import time
# import random
# s_time=time.time()
# func()
# time.sleep(random.randrange(0,10))
# tp_time=time.time()
# print("bingo time:",tp_time-s_time)
# return zs1
#
# def yuan():
# print('hello!')
#
# print(zs)
# yuan=zs(yuan)
# print(yuan)
# yuan() #先定义后执行,定义阶段就把后面的装饰器里的func指定到原来的yuan了,所以不会和后面的yuan冲突
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#定义闭包函数的基本形式 # def 外部函数名():
# 内部函数需要的变量
# def 内部函数():
# 引用外部变量
# return 内部函数 # def deco():
# x=1
# def wrapper():
# print(x)
#
# return wrapper
#
# wrapper=deco()
#
# print(wrapper) #装饰器的原理
#装饰器修订
import time
import random
#装饰器
def timmer(func):
def wrapper(*args,**kwargs): #能接收任意参数
start_time = time.time()
res=func(*args,**kwargs) #返回值
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
#被装饰函数 @timmer
def index():
time.sleep(random.randrange(1,5))
print('welecome to index page')
@timmer
def home(name):
time.sleep(random.randrange(1,3))
print('welecome to %s HOME page' %name)
return 123123123123123123123123123123123123123123 index()
dic={'x':1} #函数内改可变类型的全局变量能够直接改,而不可变类型不能直接改,需要global,
l=[1,2,3] #换句话说,函数内可以随便定义不可变类型的变量,不用关心是否和全局冲突
salary=1000
def foo():
# print(dic)
dic['x']=2
dic['y']=3
l.append(4)
global salary
salary=0
foo()
print(dic)
print(l)
print(salary)
#===运行结果====================
D:\Python36\python.exe D:/py/test/kong2.py
{'x': 2, 'y': 3}
[1, 2, 3, 4]
0
login_dic={ #全局的可变类型,可以在函数内直接改,不可变类型,需要申明global,因为不可变类型更改值需要重新开内存空间,本身就相当于一个新变量
'user':None, #如上一个例子
'status':False,
}
def auth(func):
def wrapper(*args,**kwargs):
if login_dic['user'] and login_dic['status']:
res = func(*args, **kwargs)
return res
name=input('your name: ')
password=input('your password: ')
with open(db_path,'r',encoding='utf-8') as f:
user_dic=eval(f.read())
if name in user_dic and password == user_dic[name]:
print('login ok')
login_dic['user']=name
login_dic['status']=True
res=func(*args,**kwargs)
return res
else:
print('login err')
return wrapper
@auth #auth(index)
def index():
print('welecome to index')
@auth
def home(name):
print('welecome %s to home page' %name)
index()
home('egon')
#有参装饰器,在一般装饰器之外添加针对装饰函数的参数,增加了新参数,所以再加包一层
def deco(auth_type='file'):
def auth(func):
def wrapper(*args,**kwargs):
if auth_type == 'file':
print('文件的认证方式')
elif auth_type == 'ldap':
print('ldap认证方式')
elif auth_type == 'mysql':
print('mysql认证方式')
else:
print('不知到的认证方式')
return wrapper
return auth @deco(auth_type='abc') #@auth #index=auth(index)
def index():
print('welecome to index') @deco(auth_type='ldap')
def home(name):
print('welecome %s to home page' %name) index() home('egon')
'''
编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
'''
from urllib.request import urlopen
import os cache_path=r'C:\Users\Administrator\PycharmProjects\python5期\day8\cache.txt'
def make_cache(func):
def wrapper(*args,**kwargs):
if os.path.getsize(cache_path):
#有缓存
print('\033[45m=========>有缓存\033[0m')
with open(cache_path,'rb') as f:
res=f.read() else:
res=func(*args,**kwargs) #下载
with open(cache_path,'wb') as f: #制作缓存
f.write(res) return res return wrapper @make_cache
def get(url):
return urlopen(url).read()
func_dic={} ##直接使用地址调用函数能够跳过装饰器
def make_dic(key):
def deco(func):
func_dic[key]=func
return deco
@make_dic('index') ##可以用在隐藏函数
def f1():
print('from f1')
#-------------------------
print(func_dic)
while True:
cmd=input('>>>: ').strip()
if cmd in func_dic :
func_dic[cmd]()
#======执行情况==============
D:\Python36\python.exe D:/py/test/kong2.py
{'index': <function f1 at 0x000002DE9900C9D8>}
>>>: index
from f1
>>>:
装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
>>> def now():
... print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25
函数对象有一个__name__属性,可以拿到函数的名字:
>>> now.__name__
'now'
>>> f.__name__
'now'
现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
@log
def now():
print('2015-3-25')
调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
>>> now()
call now():
2015-3-25
把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
这个3层嵌套的decorator用法如下:
@log('execute')
def now():
print('2015-3-25')
执行结果如下:
>>> now()
execute now():
2015-3-25
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。
以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper':
>>> now.__name__
'wrapper'
因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
或者针对带参数的decorator:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。
小结
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。
再思考一下能否写出一个@log的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
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 ...
随机推荐
- TDS协议解析
文章来自:http://freetds.cvs.sourceforge.net/*checkout*/freetds/freetds/doc/tds.html 该网站是免费的专门介绍TDS协议的,网址 ...
- Oracle Tuning ( instance 级别 ) 01
Shared Pool Tuning 目标是提高命中率, 以减少 I/O 操作 shared pool : 是由 library cache, data dictionary cache 两部分组成. ...
- a标签点击事件
onclick="detail(this,'${vo.id}')" function detail(obj,id){ var lb = $("#lb").val ...
- php缓存技术基础知识
普遍缓存技术 数据缓存:这里所说的数据缓存是指数据库查询缓存,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据,并把查询结果序列化后保存到文件中,以后同样的查询 ...
- UE对话框
// Put your "OnButtonClicked" stuff here FText DialogText = FText::Format( LOCTEXT("P ...
- cxGrid 显示行号及行号列列名
cxGrid默认不显示行号,但是可以通过cxGrid1DBTableView1CustomDrawIndicatorCell事件来重绘行号 选中cxGrid1DBTableView1,在OnCusto ...
- 用vector构造自动扩容的二维数组
#include <iostream> #include <string> #include <vector> using namespace std; int m ...
- iOS 保存异常日志
// // AppDelegate.m // test // // Created by Chocolate. on 14-4-16. // Copyright (c) 2014年 redasen. ...
- iOS 遍历控件
NSArray *subviews = [_bgImageView subviews]; for (id objInput in subviews) { if ([objInput isKindOfC ...
- java的list转map
companyList = companyManager.listByCompanyId(companyIds);departList = departManager.findByTree(depar ...
