第一步:最简单的函数,准备附加额外功能
def myfunc():
print("myfunc() 正在被执行")
myfunc()
运行结果:
myfunc() 正在被执行
第二步:使用装饰函数在函数执行前和执行后分别附加额外功能
# deco 函数接受一个函数 func 作为参数,并且 deco 为 func 加上了新方法
def deco(func):
print("myfunc() 未被执行")
func()
print("myfunc() 已被执行")
return func
def myfunc():
print("myfunc() 正在被执行")
myfunc = deco(myfunc)
运行结果:
myfunc() 未被执行
myfunc() 正在被执行
myfunc() 已被执行
第三步:使用语法糖@来装饰函数
def deco(func):
print("myfunc() 未被执行")
func()
print("myfunc() 已被执行")
return func
@deco
def myfunc():
print("myfunc() 正在被执行")
myfunc()
print('='*10)
myfunc()
执行结果:
myfunc() 未被执行
myfunc() 正在被执行
myfunc() 已被执行
myfunc() 正在被执行
==========
myfunc() 正在被执行
使用语法糖@来装饰函数,相当于 “myfunc = deco(myfunc)”,但新函数只在第一次被调用,且原函数多调用了一次。
第四步:使用内嵌包装函数来确保每次新函数都被调用
def deco(func):
def _deco():
print("myfunc() 未被执行")
func()
print("myfunc() 已被执行")
# 不需要返回func,实际上应返回原函数的返回值
return _deco
@deco
def myfunc():
print("myfunc() 正在被执行")
myfunc()
print('='*10)
myfunc()
执行结果:
myfunc() 未被执行
myfunc() 正在被执行
myfunc() 已被执行
==========
myfunc() 未被执行
myfunc() 正在被执行
myfunc() 已被执行
使用内嵌包装函数可以确保每次新函数都被调用,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象。
第五步:对带参数的函数进行装饰
def deco(func):
def _deco(a, b):
print("myfunc() 未被执行")
ret = func(a, b)
print("myfunc() 已被执行" + '结果:' + str(ret))
return ret
return _deco
@deco
def myfunc(a, b):
print("myfunc() 正在被执行,收到的参数:" + str(a) + ',' + str(b))
return a + b
myfunc(1, 2)
print('='*10)
myfunc(3, 4)
执行结果:
myfunc() 未被执行
myfunc() 正在被执行,收到的参数:1,2
myfunc() 已被执行结果:3
==========
myfunc() 未被执行
myfunc() 正在被执行,收到的参数:3,4
myfunc() 已被执行结果:7
我们来尝试用装饰器干点有意义的事,比如做一个脏话过滤器:
def ban_dirty_word(func):
def _deco(a):
if 'fuck' in a:
print('不能使用脏话')
else:
return func(a)
return _deco
@ban_dirty_word
def say(x):
print(x)
say('hello')
say('fuck you')
运行结果:
hello
不能使用脏话
第六步:对参数数量不确定的函数进行装饰
def deco(func):
def _deco(*args, **kwargs):
print(func.__name__ + ' 未被执行')
ret = func(*args, **kwargs)
print(func.__name__ + ' 已被执行,' + '结果:' + str(ret))
return ret
return _deco
@deco
def myfunc(a, b):
print("myfunc() 正在被执行,收到参数:%s,%s" % (a, b))
return a+b
@deco
def myfunc2(a, b, c):
print("myfunc() 正在被执行,收到参数:%s,%s,%s" % (a, b, c))
return a+b+c
myfunc(1, 2)
print('='*10)
myfunc2(1, 2, 3)
运行结果:
myfunc 未被执行
myfunc() 正在被执行,收到参数:1,2
myfunc 已被执行,结果:3
==========
myfunc2 未被执行
myfunc() 正在被执行,收到参数:1,2,3
myfunc2 已被执行,结果:6
对参数数量不确定的函数进行装饰,参数用(*args, **kwargs),自动适应变参和命名参数
第七步:让装饰器带参数
def deco(deco_arg):
def _deco(func):
def __deco(*args, **kwargs):
print(func.__name__ + '未被执行,' + '装饰器参数:' + deco_arg)
ret = func(*args, **kwargs)
print(func.__name__ + '已被执行,' + '装饰器参数:' + deco_arg)
return ret
return __deco
return _deco
@deco("my_arg")
def myfunc(x):
print("myfunc() 正在被执行, 收到参数:" + x)
myfunc('xxx')
运行结果:
myfunc未被执行,装饰器参数:my_arg
myfunc() 正在被执行, 收到参数:xxx
myfunc已被执行,装饰器参数:my_arg
我们再来尝试写一个带参数的脏话过滤器:
def deco(deco_arg): # 传入装饰器的参数
def _deco(func): # 传入要装饰的函数
def __deco(words): # 被装饰的函数的参数
if deco_arg in words:
print('不可使用脏话')
else:
ret = func(words)
return ret
return __deco
return _deco
@deco("fuck")
def say(words):
print(words)
@deco("shit")
def say2(words):
print(words)
say('hello')
say('fuck')
say2('shit')
运行结果:
hello
不可使用脏话
不可使用脏话