Python_装饰器(Decorator)

装饰器

含义:装饰器就是把原函数包装一下,对原函数进行加工,添加一些附加功能,装饰器本身就是一个函数,就是将被装饰的函数当作参数传递给装饰器,返回包装后的函数。

饰器是一个函数, 它需要返回一个新的function.这个新的函数一般不会修改被修饰函数的返回结果.

执行时机

1在模块加载时就会执行修饰器函数

2生成一个个被修饰的新的函数.

缺点:屏蔽了原函数的元信息, 例如__name__, __doc__. 如果要保留原函数的信息, 可使用标准库中的修饰器functools.wrap:

补充:

1. “@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。

换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。有点儿类似JavaScript里面的 function a (function () { ... });

2. 解释器读到这样的修饰之后,先解析@后的内容,直接就把@下一行的函数或者类作为 @ 后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象. 即: 从第一个函数修饰符开始,自下而上做参数传递,实际上是使用约定的函数修饰符达到函数嵌套的目的.

需要注意的

1. 函数先定义,再修饰它;反之会编译器不认识;

2. 修饰符“@”后面必须是之前定义的某一个函数;

3. 每个函数只能有一个修饰符,大于等于两个则不可以。

4. 不允许和函数定义在同一行

5. 只可以在模块或类定义层内对函数进行修饰,不允许修饰一个类。

1比如:多个decorator

@decorator_one
@decorator_two
def func():
    pass

相当于:

func = decorator_one(decorator_two(func))

2.比如:带参数的decorator:

@decorator(arg1, arg2)
def func():
    pass

相当于:

func = decorator(arg1,arg2)(func) 

这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”

EG1:装饰器修饰装饰器

def funA(a):
    print ('funA')

def funB(b):
    print ('funB')
def funC(c):
    print(c.__name__)
    print ('funC')

@funA
@funB
@funC
def funD():
    print ('funD')

实行结果:

funD

funC

funB

funA

解析:遇到@解释器读到这样的修饰之后,先解析@后的内容,直接就把@下一行的函数或者类作为 @ 后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象. ,第二行也是@,在去第三行,还是@,去第四行,不是,调用第三行的func方法(此时func方法中的形参cfunD方法的引用,打印的c. __name__就是“fund“,在调用第二行funB,第一行funC

注意:1.代码的执行顺序:距离被装饰的函数最近的装饰器开始执行,依次往上

  2.代码的打印顺序是从上往下执行

EG2(用类作为修饰器修饰函数)

class myDecorator(object):

    def __init__(self, fn):
        print("inside myDecorator.__init__()")
        self.fn = fn

    def __call__(self):
        self.fn()
        print("inside myDecorator.__call__()")


@myDecorator
def aFunction():
    print("inside aFunction()")


print("Finished decorating aFunction()")
aFunction()

实行结果:

inside myDecorator.__init__()

Finished decorating aFunction()

inside aFunction()

inside myDecorator.__call__()

补充:类中的方法作为装饰器(魔法方法必须使用__call__)

class A():
    def __call__(self,fnc):
        self.fnc = fnc
        return self.play
    def play(self):
        print("AAAAAA")
        self.fnc()
        print("BBBBBB")
@A()
def sleep():
    print("zzzZZ~~~~")

sleep()

实行结果:

AAAAAA
zzzZZ~~~~
BBBBBB

EG3:

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("Hao Chen"))

实行结果:

<b class='bold_css'><i class='italic_css'>Hello, Hao Chen</i></b>

上面这段代码中,需要注意这几点:
1)如果decorator有参数的话,__init__() 成员就不能传入fn了,而fn是在__call__的时候传入的。
2)这段代码还展示了 wrapped(*args, **kwargs) 这种方式来传递被decorator函数的参数。(其中:args是一个参数列表,kwargs是参数dict)

EG4:

'''对带参数的函数进行装饰,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''


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)
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

EG5:

'''对参数数量不确定的函数进行装饰,参数用(*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(a, b):
    print(" myfunc(%s,%s) called." % (a, b))
    return a+b

@deco
def myfunc2(a, b, c):
    print(" myfunc2(%s,%s,%s) called." % (a, b, c))
    return a+b+c

myfunc(1, 2)
myfunc(3, 4)
myfunc2(1, 2, 3)
myfunc2(3, 4, 5)

实行结果:

before myfunc called.

 myfunc(1,2) called.

  after myfunc called. result: 3

before myfunc called.

 myfunc(3,4) called.

  after myfunc called. result: 7

before myfunc2 called.

 myfunc2(1,2,3) called.

  after myfunc2 called. result: 6

before myfunc2 called.

 myfunc2(3,4,5) called.

  after myfunc2 called. result: 12

用Decorator设置函数的调用参数(三种方法)

第一种,通过 **kwargs,这种方法decorator会在kwargs中注入参数。

def decorate_A(function):
    def wrap_function(*args, **kwargs):
        kwargs['str'] = 'Hello!'
        return function(*args, **kwargs)
    return wrap_function

@decorate_A
def print_message_A(*args, **kwargs):
    print(kwargs['str'])

print_message_A()

实行结果:

Hello!

第二种,约定好参数,直接修改参数

def decorate_B(function):
    def wrap_function(*args, **kwargs):
        str = 'Hello!'
        return function(str, *args, **kwargs)
    return wrap_function

@decorate_B
def print_message_B(str, *args, **kwargs):
    print(str)

print_message_B()

实行结果:

Hello!

第三种,通过 *args 注入

def decorate_C(function):
    def wrap_function(*args, **kwargs):
        str = 'Hello!'
        #args.insert(1, str)
        args = args +(str,)
        return function(*args, **kwargs)
    return wrap_function

class Printer:
    @decorate_C
    def print_message(self, str, *args, **kwargs):
        print(str)

p = Printer()
p.print_message()

实行结果:

Hello!

EG:

class Sleep:
    # 外层函数
    def __init__(self, *args):
        # 如果有参数就存入对象方便其他方法使用
        self.args = args

    # 装饰器函数
    def __call__(self, func):
        # 将基本函数存入对象
        self.func = func

        # 返回inner函数
        return self.inner

    # 装饰器函数
    def inner(self):
        # 扩展功能1
        print('-----start')
        # 基本函数功能
        self.func()
        # 扩展功能2
        print('-----end')


@Sleep('la')  # @Sleep() -> @对象  # @装饰器函数
def sleep():
    print('ZZZzzz...')
    
sleep()

实行结果:

-----start

ZZZzzz...

-----end

 

Decorator的副作用(正在补充)

 

参考文档:

https://blog.csdn.net/scdxmoe/article/details/49637629

https://coolshell.cn/articles/10822.html

http://www.open-open.com/lib/view/open1395285030019.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值