函数装饰器
先来看个例子:
def foo(*args, **kwargs):
print 'args = ', args
print 'kwargs = ', kwargs
print '---------------------------------------'
if __name__ == '__main__':
foo(1,2,3,4)
foo(a=1,b=2,c=3)
foo(1,2,3,4, a=1,b=2,c=3)
foo('a', 1, None, a=1, b='2', c=3)
输出结果如下:
args = (1, 2, 3, 4)
kwargs = {}
---------------------------------------
args = ()
kwargs = {'a': 1, 'c': 3, 'b': 2}
---------------------------------------
args = (1, 2, 3, 4)
kwargs = {'a': 1, 'c': 3, 'b': 2}
---------------------------------------
args = ('a', 1, None)
kwargs = {'a': 1, 'c': 3, 'b': '2'}
---------------------------------------
可以看到,这两个是python中的可变参数。args表示任何多个无名参数,它是一个tuple;**kwargs表示关键字参数,它是一个dict。并且同时使用args和kwargs时,必须*args参数列要在kwargs前,像foo(a=1, b=‘2’, c=3, a’, 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。
def use_logging(func):
def wrapper(*args,**kwargs):
logging.warning("%s is running" % func.__name__)
return func(*args)
return wrapper
@use_logging
def foo():
print("i am foo")
@use_logging
def bar():
print('i am bar')
# bar=use_logging(bar)
bar()
foo()
这里的大概逻辑:
有个功能(logging.warning),两个函数foo和bar都会用到;
但是又不想多写,于是写成下面形式了:
在调用foo的时候,执行了logging.warning这个功能;
在调用bar的时候,也执行了logging.warning这个功能;
但是只写了一遍!
def use_logging(level):
def decorateor(func):
def wrapper(*args, **kwargs):
if level =="warn":
logging.warning("%s is running" % func.__name__)
else:
print("hello !")
return func(*args)
return wrapper
return decorateor
@use_logging(level="warn")
def foo(name="foo"):
print("i am %s"%name)
@use_logging(level="hello")
def bar(name="bar"):
print('i am %s'%name)
foo()
bar()
带参数的装饰器:
大体逻辑同上;
use_logging是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。
我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level=“warn”)调用的时候,
Python能够发现这一层的封装,并把参数传递到装饰器的环境中。
类装饰器
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print('class decorator runing')
self._func()
print('class decorator ending')
@Foo
def bar():
print ('bar')
@Foo
def hi():
print("hi")
bar()
hi()
类装饰器:
再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。
使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
缺点
缺点:
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,
比如函数的docstring、name、参数列表,先看例子:
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
return x+x*x
f = logged(f)
"""
with_logging was called
f was called
"""
def f(x):
return x+x*x
f = logged(f)
"""
f was called
"""
f(2)
这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
print("this is f(x)")
return x + x * x
f(2)
print("=================================")
print(f.__name__)
print("=================================")
print(f.__doc__)
装饰器的顺序
@a
@b
@c
def f ():
等效于:
f = a(b(c(f)))
当装饰器也需要参入参数时我们需要给装饰器再加一层函数,
此时装饰器接受到的方法需要进入第二层函数进行接受,
第一层需要接受装饰器自己的参数
user, password = 'db', '12345'
def login(login_type):
def outer_wrapper(func):
def wrapper(*agr1, **kwargs):
usernameInput = input("UserName:").strip()
passwordInput = input("Password:").strip()
if login_type == "local":
if user == usernameInput and password == passwordInput:
print("login successful")
res = func(*agr1, **kwargs) # 接受返回结果
return res
else:
print("login fail")
elif login_type == "ldap":
print("远程登录")
return wrapper
return outer_wrapper
def index():
print("welcome to index page")
@login(login_type="local") # 对装饰分类
def home():
print("welcome to home page")
return "from home"
@login(login_type="ldap") # 对装饰分类
def blog():
print("welcome to blog page")
index()
print(home())
blog()
参考
https://blog.csdn.net/qq_38520096/article/details/79254901