python 装饰器学习笔记
装饰器的基本原理和语法
- 在这里仍旧强调在python 中一切皆对象,可以用来赋值等操作
def mvp_player():
print("the MVP of NBA in 2019 is Kawai")
copy_mvp_player=mvp_player
mvp_player()
print("*"*10)
copy_mvp_player()
output:
the MVP of NBA in 2019 is Kawai
the MVP of NBA in 2019 is Kawai
2. 函数的内部可以定义函数
def get_mvp():
print("enterget_mvp func")
def mvp_player():
print("the MVP of NBA in 2019 is Kawai")
return mvp_player()
get_mvp()
可以使用另外一种方法,返回的是函数的名字(C中的函数指针)
def get_mvp():
print("enterget_mvp func")
def mvp_player():
print("the MVP of NBA in 2019 is Kawai")
return mvp_player
test_player=get_mvp()
test_player()
3. 根据2 中,此时函数可以添加参数,参数可以是任何对象,当然包括函数
def get_mvp(func):
print("enterget_mvp func")
def wrapper():
print("the MVP of NBA in 2019 is Kawai")
func()
return wrapper
def func():
print("Wooooooooooo")
test_player=get_mvp(func)
test_player()
这里就有了装饰器的基本原型,通过一个函数的包装,是的func函数在执行前或者执行后不做任何修改。
但是当你需要每次运行函数func 的时候,最简单的方法就是每次都使用get_mvp函数包装。这就是装饰器的工作原理。
4. 在python 中有特殊的语法来简化这个步骤
def get_mvp(func):
print("enterget_mvp func")
def wrapper():
print("the MVP of NBA in 2019 is Kawai")
func()
return wrapper
@get_mvp
def func():
print("Wooooooooooo")
func()
即,通过@get_mvp 来替换func=get_mvp(func)
5. 可以通过多个装饰器来装饰某个函数
def bread(func):
def wrapper():
print("</''''''\>")
func()
print("<\______/>")
return wrapper
def ingredients(func):
def wrapper():
print("#tomatoes#")
func()
print("~salad~")
return wrapper
@bread
@ingredients
def sandwich(food="--ham--"):
print(food)
sandwich()
output:
</’’’’’’>
#tomatoes#
–ham–
salad
<______/>
以上相当于:
sandwich = bread(ingredients(sandwich))
sandwich()
装饰器的高级语法
- 传递参数给装饰器
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(function_arg1, function_arg2) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
print("*"*50)
decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
**************************************************
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Sheldon
# - from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
这里看到,装饰器函数decorator_maker_with_arguments 有两个参数被传递了,被装饰的函数decorated_function_with_arguments有两个参数,如果你愿意可以使用*args,**kwargs来定义参数,这样能够修饰所有不同参数的函数。
注意:
- 装饰器只能被调用一次。 当你import脚本的时候,装饰器就已经产生,不能够再修改装饰器参数。
"""try3.py"""
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(function_arg1, function_arg2) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
>>> import try3
I make decorators! And I accept arguments: Leonard, Sheldon
I am the decorator. Somehow you passed me arguments: Leonard, Sheldon
- 修改上面例子中的装饰器中函数的参数为*args,**kwargs
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(*args, **kwargs) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {} {}\n"
"\t- from the function call: {} {}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
args,kwargs))
return func(*args, **kwargs)
return wrapped
return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
@decorator_maker_with_arguments("James", "Harden")
def decorated_function_with_arg_1():
print("I am the decorated function and only knows about my arguments: without arg")
print("*"*50)
decorated_function_with_arguments("Rajesh", "Howard")
print("*"*50)
decorated_function_with_arg_1()
output :
I make decorators! And I accept arguments: Leonard, Sheldon
I am the decorator. Somehow you passed me arguments: Leonard, Sheldon
I make decorators! And I accept arguments: James, Harden
I am the decorator. Somehow you passed me arguments: James, Harden
**************************************************
I am the wrapper around the decorated function.
I can access all the variables
- from the decorator: Leonard Sheldon
- from the function call: ('Rajesh', 'Howard') {}
Then I can pass them to the decorated function
I am the decorated function and only knows about my arguments: Rajesh Howard
**************************************************
I am the wrapper around the decorated function.
I can access all the variables
- from the decorator: James Harden
- from the function call: () {}
Then I can pass them to the decorated function
I am the decorated function and only knows about my arguments: without arg
理解:
对于一个最基本的装饰器为
def decorateor_func(func):
def wrapper(*args,**kwargs): -->这里的参数指的是被装饰的函数(func)的
func(*args,**kwargs)
return wrapper
如果装饰器需要传入参数需要在此基础上再包装一层函数
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
#this is a basic decorator
def my_decorator(func): # func is to be decorated func
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(*args, **kwargs) : # this parameter is for func 's parameter
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {} {}\n"
"\t- from the function call: {} {}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
args,kwargs))
return func(*args, **kwargs)
return wrapped
return my_decorator
- 修饰装饰器的装饰器
def decorator_with_args(decorator_to_enhance):
"""
This function is supposed to be used as a decorator.
It must decorate an other function, that is intended to be used as a decorator.
Take a cup of coffee.
It will allow any decorator to accept an arbitrary number of arguments,
saving you the headache to remember how to do that every time.
"""
# We use the same trick we did to pass arguments
def decorator_maker(*args, **kwargs): # this arg is for decorator_to_enhance 's arg
# We create on the fly a decorator that accepts only a function
# but keeps the passed arguments from the maker.
def decorator_wrapper(func): # this func is the final decorated func
# We return the result of the original decorator, which, after all,
# IS JUST AN ORDINARY FUNCTION (which returns a function).
# Only pitfall: the decorator must have this specific signature or it won't work:
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker
# You create the function you will use as a decorator. And stick a decorator on it :-)
# Don't forget, the signature is "decorator(func, *args, **kwargs)"
@decorator_with_args
def decorated_decorator(func, *args, **kwargs):
def wrapper(*farg, **fkwarg): # this farg/fkwarg is for func's parameter ,should differ from the decorator's parameter
print("Decorated with {0} {1}".format(args, kwargs)) # this is to print the decorator's parameter
return func(*farg, **fkwarg)
return wrapper
# Then you decorate the functions you wish with your brand new decorated decorator.
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
print("Hello {0} {1}".format(function_arg1, function_arg2))
decorated_function("Universe and", "everything")
上面的code可以简化为下面一个装饰器函数:
def decorated_test(*dargs,**dkwargs):
#print("Decorated with {0}".format(tag))
def decorator_maker(func):
def wrapper(*args,**kwargs):
print("Decorated with {0} {1}".format(dargs,dkwargs))
func(*args,**kwargs)
return wrapper
return decorator_maker
@decorated_test(42,404,1024,name='bob')
def decorated_function1(function_arg1, function_arg2):
print("Hello {0} {1}".format(function_arg1, function_arg2))
print("*"*50)
decorated_function1("james","harden")
装饰器带来的debug问题
- Decorators were introduced in Python 2.4, so be sure your code will be run on >= 2.4.
- Decorators slow down the function call. Keep that in mind.
- You cannot un-decorate a function. (There are hacks to create decorators that can be removed, but nobody uses them.) So once a function is decorated, it’s decorated for all the code.
- Decorators wrap functions, which can make them hard to debug. (This gets better from Python >= 2.5; see below.)
- The functools module was introduced in Python 2.5. It includes the function functools.wraps(), which copies the name, module, and docstring of the decorated function to its wrapper.
我们知道当使用一个装饰器的时候
def decorator_f(func):
def wrapper():
func()
return wrapper
@decorator_f
def func():
pass
print(func.__name__)
func() #--> decorator_f(func)()
output :
wrapper
此时发现实际上的func 已经被改为wrapper,当我们需要debug func时其实已经获取到的属性已经改变
此时可以通过functools中的wraps函数来维持func原本的属性。这里的wraps其实也是一个装饰器
from functools import wraps
def decorator_f(func):
@wraps(func)
def wrapper():
func()
return wrapper
@decorator_f
def func():
pass
print(func.__name__)
output :
func
简单的装饰器的多种写法
eg1
from functools import wraps
def makebold(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"
return wrapped
def makeitalic(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<i>" + fn(*args, **kwargs) + "</i>"
return wrapped
@makebold
@makeitalic
def say():
return 'Hello'
print(say()) # -> <b><i>Hello</i></b>
eg2
def html_deco(tag):
def decorator(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return '<%s>' % tag + fn(*args, **kwargs) + '</%s>' % tag
return wrapped
return decorator
@html_deco('b')
@html_deco('i')
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
或者合并
makebolditalic = lambda fn: makebold(makeitalic(fn))
@makebolditalic
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
为了简化代码,去掉冗余的code
def multi_html_deco(*tags):
start_tags, end_tags = [], []
for tag in tags:
start_tags.append('<%s>' % tag)
end_tags.append('</%s>' % tag)
start_tags = ''.join(start_tags)
end_tags = ''.join(reversed(end_tags))
def decorator(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return start_tags + fn(*args, **kwargs) + end_tags
return wrapped
return decorator
makebolditalic = multi_html_deco('b', 'i')
@makebolditalic
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
def makeitalic(fn):
return lambda: '<i>' + fn() + '</i>'
def makebold(fn):
return lambda: '<b>' + fn() + '</b>'
@makebold
@makeitalic
def say():
return 'Hello'
通过类来定义装饰器,
- 使用__call__函数
class sty(object):
def __init__(self, tag):
self.tag = tag
def __call__(self, f):
def newf():
return "<{tag}>{res}</{tag}>".format(res=f(), tag=self.tag)
return newf
@sty('b')
@sty('i')
def sayhi():
return 'hi'
class makeHtmlTagClass(object):
def __init__(self, tag, css_class=""):
self._tag = tag
self._css_class = " class='{0}'".format(css_class) \
if css_class != "" else ""
def __call__(self, fn):
def wrapped(*args, **kwargs):
return "<" + self._tag + self._css_class+">" \
+ fn(*args, **kwargs) + "</" + self._tag + ">"
return wrapped
@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
return "Hello, {}".format(name)
print hello("Your name")
下面的这种可能更加OOP一些,但是需要特别理解
from abc import ABCMeta, abstractclassmethod
class Decorator(metaclass=ABCMeta):
""" Acts as a base class for all decorators """
def __init__(self):
self.method = None
def __call__(self, method):
self.method = method
print(self.method.__name__)
return self.call
@abstractclassmethod
def call(self, *args, **kwargs):
return self.method(*args, **kwargs)
class MakeBold(Decorator):
def call(self):
print(4)
return "<b>" + self.method() + "</b>"
class MakeItalic(Decorator):
def call(self):
return "<i>" + self.method() + "</i>"
print("*"*50)
@MakeBold()
@MakeItalic()
def say():
return "Hello"
print(say())
reference
https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators