Python装饰器学习(九步入门):http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
浅谈Python装饰器:https://blog.csdn.net/mdl13412/article/details/22608283
Python装饰器与面向切面编程:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
一些更加实用的 Python 装饰器示例:https://sikasjc.github.io/2018/09/29/somedecorator/
Python装饰器高级版—Python类内定义装饰器并传递self参数:https://blog.51cto.com/yishi/2354752
Python 装饰器学习(九步入门)
这是在 Python学习小组上介绍的内容,现学现卖、多练习是好的学习方式。
第一步:最简单的函数,准备附加额外功能
示例代码:
# -*- coding:gbk -*-
'''示例1: 最简单的函数,表示调用了两次'''
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
def myfunc():
print(" myfunc() called.")
print('*****************************')
dec_func = deco(myfunc)
print('*****************************')
dec_func()
"""
结果:
*****************************
before myfunc() called.
myfunc() called.
after myfunc() called.
*****************************
myfunc() called.
"""
第二步:使用装饰函数在函数执行前和执行后分别附加额外功能
示例代码:
# -*- coding:gbk -*-
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
def myfunc():
print(" myfunc() called.")
dec_func = deco(myfunc)
print('****************************')
dec_func()
dec_func()
"""
结果:
before myfunc() called.
myfunc() called.
after myfunc() called.
****************************
myfunc() called.
myfunc() called.
"""
第三步:使用语法糖@来装饰函数
本例中 deco 没有使用内嵌函数,可以看到第一次执行可以进入到装饰函数,但是第二次不会进入装饰函数
# -*- coding:gbk -*-
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
@deco
def myfunc():
print(" myfunc() called.")
# 第一次调用后,返回的是 deco 里面的 func 对象,
# 所以第二次调用myfunc() 只输出 myfunc() called.
myfunc() # 第一次调用
print('************************')
myfunc() # 第二次调用
"""
结果:
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
************************
myfunc() called.
"""
第四步:使用 内嵌包装函数 来确保 每次 新函数 都被调用( 被装饰的函数没有参数 )
装饰函数 deco 返回内嵌包装函数对象 _deco。使用内嵌包装函数来确保每次新函数都被调用
# -*- coding:gbk -*-
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco():
print("before myfunc() called.")
func()
print("after myfunc() called.")
# 不需要返回func,实际上应返回原函数的返回值
return _deco
@deco
def myfunc():
print(" myfunc() called.")
return 'ok'
myfunc()
print('*************************')
myfunc()
"""
执行结果:
before myfunc() called.
myfunc() called.
after myfunc() called.
*************************
before myfunc() called.
myfunc() called.
after myfunc() called.
"""
示例代码:无参数的函数( 被装饰的函数没有参数 )
# decorator.py
from time import ctime, sleep
def time_fun(func):
def wrapped_func():
print(f"{func.__name__} called at {ctime()}")
return func()
return wrapped_func
@time_fun
def foo():
pass
foo()
sleep(2)
foo()
第五步:对 带参数的函数 进行装饰( 被装饰的函数带参数 )
示例代码:
# -*- coding:gbk -*-
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print("after myfunc() called. result: %s" % ret)
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
print('*************************')
myfunc(3, 4)
"""
执行结果:
before myfunc() called.
myfunc(1,2) called.
after myfunc() called. result: 3
*************************
before myfunc() called.
myfunc(3,4) called.
after myfunc() called. result: 7
"""
示例代码:被装饰的函数带参数
# decorator2.py
from time import ctime, sleep
def time_fun(func):
def wrapped_func(a, b):
print(f"{func.__name__} called at {ctime()}")
print(a, b)
return func(a, b)
return wrapped_func
@time_fun
def foo(a, b):
print(a + b)
foo(3, 5)
sleep(2)
foo(2, 4)
第六步:对 参数数量不确定 的函数进行装饰
示例代码:
# -*- coding:gbk -*-
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''
def deco(func):
def _deco(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print("after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco
@deco
def myfunc_1(a, b):
print(" myfunc_1(%s,%s) called." % (a, b))
return a + b
@deco
def myfunc_2(a, b, c):
print(" myfunc_2(%s,%s,%s) called." % (a, b, c))
return a + b + c
print('*' * 30)
myfunc_1(1, 2)
print('*' * 30)
myfunc_1(3, 4)
print('*' * 30)
myfunc_2(1, 2, 3)
print('*' * 30)
myfunc_2(3, 4, 5)
"""
执行结果:
******************************
before myfunc_1 called.
myfunc_1(1,2) called.
after myfunc_1 called. result: 3
******************************
before myfunc_1 called.
myfunc_1(3,4) called.
after myfunc_1 called. result: 7
******************************
before myfunc_2 called.
myfunc_2(1,2,3) called.
after myfunc_2 called. result: 6
******************************
before myfunc_2 called.
myfunc_2(3,4,5) called.
after myfunc_2 called. result: 12
"""
第七步:让 装饰器 带 参数
示例代码:
# -*- coding:gbk -*-
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print("after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc_1() called.")
@deco("module2")
def myfunc2():
print(" myfunc_2() called.")
print('************************************')
myfunc()
print('************************************')
myfunc2()
"""
执行结果:
************************************
before myfunc called [mymodule].
myfunc_1() called.
after myfunc called [mymodule].
************************************
before myfunc2 called [module2].
myfunc_2() called.
after myfunc2 called [module2].
"""
装饰器带参数,在原有装饰器的基础上,设置外部变量
from time import ctime, sleep
def time_fun_arg(pre="hello"):
def time_fun(func):
def wrapped_func():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
return func()
return wrapped_func
return time_fun
@time_fun_arg("12345")
def foo():
pass
@time_fun_arg("abcde")
def too():
pass
foo()
sleep(2)
foo()
too()
sleep(2)
too()
第八步:让 装饰器 带 类 参数
示例代码:
# -*- coding:gbk -*-
'''示例8: 装饰器带类参数'''
class Locker:
def __init__(self):
print("locker.__init__() should be not called.")
@staticmethod
def acquire():
print("locker.acquire() called.(这是静态方法)")
@staticmethod
def release():
print("locker.release() called.(不需要对象实例)")
def deco(cls):
'''cls 必须实现acquire和release静态方法'''
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, cls))
cls.acquire()
try:
return func()
finally:
cls.release()
return __deco
return _deco
@deco(Locker)
def myfunc():
print(" myfunc() called.")
print('*********************************************')
myfunc()
print('*********************************************')
myfunc()
"""
执行结果:
*********************************************
before myfunc called [<class '__main__.Locker'>].
locker.acquire() called.(这是静态方法)
myfunc() called.
locker.release() called.(不需要对象实例)
*********************************************
before myfunc called [<class '__main__.Locker'>].
locker.acquire() called.(这是静态方法)
myfunc() called.
locker.release() called.(不需要对象实例)
"""
装饰器 和 闭包 混用:
# coding=utf-8
from time import time
def logged(when):
def log(f, *args, **kargs):
print("fun:%s args:%r kargs:%r" % (f, args, kargs))
# %r字符串的同时,显示原有对象类型
def pre_logged(f):
def wrapper(*args, **kargs):
log(f, *args, **kargs)
return f(*args, **kargs)
return wrapper
def post_logged(f):
def wrapper(*args, **kargs):
now = time()
try:
return f(*args, **kargs)
finally:
log(f, *args, **kargs)
print("time delta: %s" % (time() - now))
return wrapper
try:
return {"pre": pre_logged, "post": post_logged}[when]
except BaseException as e:
print('must be "pre" or "post"')
raise e
@logged("post")
def fun(name):
print("Hello, ", name)
fun("world!")
第九步:装饰器带类参数,并分拆公共类到其他py文件中,
同时 演示了对一个函数应用多个装饰器
mylocker.py
# -*- coding:gbk -*-
class MyLocker:
def __init__(self):
print("mylocker.__init__() called.")
@staticmethod
def acquire():
print("mylocker.acquire() called.")
@staticmethod
def unlock():
print("mylocker.unlock() called.")
class LockerEx(MyLocker):
@staticmethod
def acquire():
print("lockerex.acquire() called.")
@staticmethod
def unlock():
print("lockerex.unlock() called.")
def lock_helper(cls):
"""cls 必须实现acquire和release静态方法"""
def _deco(func):
def __deco(*args, **kwargs):
print("before %s called." % func.__name__)
cls.acquire()
try:
return func(*args, **kwargs)
finally:
cls.unlock()
return __deco
return _deco
测试代码:
# -*- coding:gbk -*-
"""
示例 9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器
"""
class Example:
@lock_helper(MyLocker)
def func_1(self):
print("\nfunc_1() called.")
@lock_helper(MyLocker)
@lock_helper(LockerEx)
def func_2(self, a, b):
print("\nfunc_2() called.")
return a + b
if __name__ == "__main__":
a = Example()
a.func_1()
print(a.func_1())
print(a.func_2(1, 2))
print(a.func_2(3, 4))
"""
执行结果:
before func_1 called.
mylocker.acquire() called.
func_1() called.
mylocker.unlock() called.
before func_1 called.
mylocker.acquire() called.
func_1() called.
mylocker.unlock() called.
None
before __deco called.
mylocker.acquire() called.
before func_2 called.
lockerex.acquire() called.
func_2() called.
lockerex.unlock() called.
mylocker.unlock() called.
3
before __deco called.
mylocker.acquire() called.
before func_2 called.
lockerex.acquire() called.
func_2() called.
lockerex.unlock() called.
mylocker.unlock() called.
7
"""
下面是参考资料,当初有不少地方没看明白,真正练习后才明白些:
1. Python装饰器学习:http://blog.csdn.net/thy38/article/details/4471421
2. Python装饰器与面向切面编程:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
3. Python装饰器的理解:http://apps.hi.baidu.com/share/detail/17572338
Python 装饰器的 4 种类型
- 函数 装饰 函数
- 函数 装饰 类
- 类 装饰 函数
- 类 装饰 类
@wraps(func) 要使用这个必须导入functools,这个作用是消除(被装饰后的函数名等属性的改变)副作用
参考:https://blog.csdn.net/freeking101/article/details/109662542
from functools import wraps
def decorate(src_func):
@wraps(src_func)
def wrapper():
print("hello")
src_func()
print("world")
return wrapper
@decorate
def func():
print("2019-12-31")
print(func.__name__, func.__doc__)
# 加上 @wraps(src_func) 输出为func None
# 不加 输出为 wrapper None
一:函数 装饰 函数
def wrapFun(func):
def inner(a, b):
print('function name:', func.__name__)
r = func(a, b)
return r
return inner
@wrapFun
def myadd(a, b):
return a + b
print(myadd(2, 3))
装饰不带参数的函数
# -*- coding: utf-8 -*-
def clothes(func):
def wear():
print('Buy clothes!{}'.format(func.__name__))
return func()
return wear
@clothes
def body():
print('The body feels cold!')
body()
#备注:@是语法糖
# 不用语法糖的情况下,使用下面语句也能实现装饰作用:把body再加工,再传给body
# body = clothes(body)
示例 :
def deco(func):
def _deco(*args, **kwargs):
print('call deco')
func(*args, **kwargs)
return _deco
@deco
def test():
print('call test')
# 等同于
# def test():
# print('call test')
# test = deco(func)
装饰带一个参数的函数
# -*- coding: utf-8 -*-
def clothes(func):
def wear(anything): # 实际是定义一个anything参数,对应body函数参数
print('Buy clothes!{}'.format(func.__name__))
return func(anything) # 执行func函数,并传入调用传入的anything参数
# wear = func(anything) # 在这一步,实际上可以看出来,为啥wear必须带参数,因为它就是func(anything)
return wear
# 所以clothes的结果是
# clothes = wear = func(anything)
# 不用语法糖的情况下就是
# body = clothes(body)('hands')
# 进一步证明:print(body.__name__) 显示的是wear函数
@clothes
def body(part):
print('The body feels could!{}'.format(part))
body('hands')
装饰带不定长参数的函数
通常装饰器不只装饰一个函数,每个函数参数的个数也不相同,这个时候使用不定长参数*args,**kwargs
def clothes(func):
def wear(*args, **kwargs):
print('Buy clothes!{}'.format(func.__name__))
return func(*args, **kwargs)
return wear
@clothes
def body(part):
print('The body feels could!{}'.format(part))
@clothes
def head(head_wear, num=2):
print('The head need buy {} {}!'.format(num, head_wear))
body('hands')
head('headdress')
装饰器带参数
# 把装饰器再包装,实现了seasons传递装饰器参数。
def seasons(season_type):
def clothes(func):
def wear(*args, **kwargs):
if season_type == 1:
s = 'spring'
elif season_type == 2:
s = 'summer'
elif season_type == 3:
s = 'autumn'
elif season_type == 4:
s = 'winter'
else:
print('The args is error!')
return func(*args, **kwargs)
print('The season is {}!{}'.format(s, func.__name__))
return func(*args, **kwargs)
return wear
return clothes
@seasons(2)
def children():
print('i am children')
示例:
def deco(*args, **kwargs):
def _deco(func):
print(args, kwargs)
def __deco(*args, **kwargs):
print('call deco')
func(*args, **kwargs)
return __deco
return _deco
@deco('hello', x='ni hao')
def test():
print('call test')
# 等同于
# def test():
# print('call test')
# test = deco('hello', x='ni hao')(test)
二:函数 装饰 类
示例: