python装饰器(decorator)

python 装饰器学习笔记

装饰器的基本原理和语法

  1. 在这里仍旧强调在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来定义参数,这样能够修饰所有不同参数的函数。
注意:

  1. 装饰器只能被调用一次。 当你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
  1. 修改上面例子中的装饰器中函数的参数为*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
  1. 修饰装饰器的装饰器
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'

通过类来定义装饰器,

  1. 使用__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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值