python 装饰器详解

自作多情 提交于 2020-08-09 22:27:15

装饰器

先理解一个柯里化:

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术

即 add(a,b), add(a)(b), 这两个运行效果一样

@语法糖在什么时候干了啥事? @语法糖只做一件事那就是 仅在被加载或导入时把被装饰函数传入装饰函数中并返回新的函数,且只运行一次,这点很重要一定要理解 @的后面是一个可调用的,且参数是接收一个函数的对象,即@x, x = f1(fn)比如函数的引用,或实现__call__函数的类

装饰器分解说明:

不管带不带参啥的。万变不离其中。

@a
def b():
    pass

这种的分解为: b(): b = a(b) >> b(), 此时调用的b()其实就是调用b = a(b) 然后 b(), a为装饰器函数的引用接受一个函数做参数,返回一个新函数,然后调用,如果被装饰的函数由参数直接带上就是了, 只不过a(b)返回的函数也必须是一个带参数的。如下

@a
def b(x, y):   等价于b = a(b) >> b(x, y), 即a(b) 返回了一个接受两个参数的函数
    pass

这种的装饰器应该是这样的:

def a(fn):
    def wrapper(*args, **kwargs):接受被装饰函数的参数
        do some thing
        return wrapper
    return wrapper 返回新函数,在导入是就已经执行了,所以装饰器在导入后装饰器参数和装饰的函数就不能改变的了
@a(c, d)
def b():
    pass

这种的分解为: b(): b = a(c, d)(b) >> b(), 此时调用的b()其实就是调用b = a(c, d)(b) 然后 b(), a(c, d)类似1中的a,所以这种会多一层,先返回上面1中的a,在返回新函数,再调用执行,

这种装饰器应该是这样的:

def outa(c,d): 先接受两个参数, 返回一个1上面的a, 在由@a同样的原理来执行,只是多了一步闭包而已
    def a(fn):
        def wrapper(*args, **kwargs):接受被装饰函数的参数
            do some thing
            return wrapper
        return wrapper 返回新函数,在导入是就已经执行了,所以装饰器在导入后装饰器参数和装饰的函数就不能改变的了
    return a

以下看例子


def decor(fn):
    return 2

@decor #等价于 add = decor(add),在被加载是就已经运行装饰器替换了原函数,且只运行一次
def add():
    return 1
print(add)

def decor(fn):
    def wrapper():
        """wo shi decorator"""
        return 2
    return wrapper

@decor #等价于 add = decor(add),在被加载是就已经运行装饰器替换了原函数,且只运行一次
def add():
    return 1

#此时name为wrapper,即此时的调用已经是在调用docor里的wrapper了,说明在加载时函数add已经被装饰器替换成了新函数
print(add.__name__)
print(add.__doc__) #此时的doc都被替换成了 decor的了
print(add())
# 被装饰函数带参数
def decor(fn):
#     根据需求而定参数
    def wrapper(*args, **kwargs):
        fn()
        return args
    return wrapper

@decor #等价于 add = decor(add)
def add(x, y, z):
    return 1

#此时name为wrapper,即此时的调用已经是在调用docor里的wrapper了,说明在加载时函数add已经被装饰器替换成了新函数
print(add.__name__)
rint(add.__doc__) #此时的doc都被替换成了 decor的了
print(add(1,2,3))

# 其实这边add(1,2,3)其实调用的是 decor下面的wrapper(1,2,3), 因为此时的add 就是函数 wrapper
# 其实调用顺序是: decor(add)(1,2,3)
# 装饰函数带参数
# 由上面的可以理解@decor 的作用等价于 add = docor(add)
# @后面的 docor 是函数的引用, @语法糖会自己把被装饰的函数作为参数调用@后面的函数即 add = doctor(add)
# 那么@后面只要是函数的引用就行了对吧,那是不是@后面可以跟一个函数调用,此调用返回一个函数的引用,即上面的decor函数

# outdecor函数调用后返回decor的函数引用
def outdecor(a, b):
    def decor(fn):
    #     根据需求而定参数
        def wrapper(*args, **kwargs):
            fn(*args)
            return args
        return wrapper
    return decor

@outdecor(1,2) #等价于 add = outdecor(1,2)(add)
def add(x, y, z):
    return 1

print(add.__name__)
print(add(1,2,3))

#上面的@outdecor(1,2),真实操作就是,先调用了outdecor(1,2) 返回了一个内层的函数decor的引用,这样就把装饰器的参数传进去了(由闭包实现)

# 由开发需要我们需要把add的__doc__改回自己原本的,或者还原其他属性

# 我们也可以写一个装饰器,还原属性
def restore_property(fn): #这边的fn就是接受的add函数的
    def wrapper(wrappered):# 这边的wrappered是接受 outdecor中的wrapper的, 
        #这里面没有对wrappered函数进行调用,只是改动了wrappered对属性, 所以没有接受参数对那层函数
        wrappered.__name__ = fn.__name__
        wrappered.__doc__ = fn.__doc__
        #这个return一定要, 因为在下面的@restore_property(fn)需要有一个函数 func(wrapper)
        #并返回outdecor中的wrapper, 即上面讲的@后面必需是个函数对引用,或一个函数对调用且调用后返回一个函数对引用
        return wrappered
    return wrapper


def outdecor(a, b):
    def decor(fn):
        @restore_property(fn) #装饰器里的装饰器
        def wrapper(*args, **kwargs):
            """wo shi decorator"""
            enter = fn(*args)
            return enter #这个return 可有可无, 这边主要是fn()调用的返回值, 有需要的话就要
        return wrapper #这边的wrapper 其实就是上面return 回来的wrappered
    return decor

@outdecor(1,2) #等价于 add = outdecor(1,2)(add)
def add(x, y, z):
    """wo shi add """
    return x + y + z

print(add.__name__)
print(add.__doc__)
print(add(1,2,3))



# 用类写一个带参数的装饰器, 且还原doc等属性
class RestoreProperty(object):
    def __init__(self, fn):
        self.fn = fn

    def __call__(self, wrappered):
        wrappered.__name__ = self.fn.__name__
        
        wrappered.__doc__ = self.fn.__doc__
        return wrappered

    
class Decor(object):
    def __init__(self, c, d):
        self.c = c
        self.d = d
        
    def __call__(self, fn):
        @RestoreProperty(fn)
        def wrapper(x, y, z):
            """wo shi 类装饰器"""
            print('before')
            enter = fn(x, y, z)
            print('after')
            return enter
        return wrapper



@Decor(1,2) #等价于 add = outdecor(1,2)(add)
def add(x, y, z):
    """wo shi add """
    return x + y + z

print(add.__name__)
print(add.__doc__)
print(add(1,2,3))
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!