python装饰器

1.闭包

闭包就是变量在函数里定义后,函数运行后就不会存在了,但是这个变量在函数运行中被其中的嵌套函数引用了,就留在了嵌套函数里面,这就是闭。闭包是理解装饰器的基础。

#闭包
def main():
    msg = 'i am writing log'
    #嵌套函数
    def mylog():
        print(msg)
    return mylog
colosure = main()
#这里没有输出
colosure()
#output:i am writing log

2.闭包转为装饰器

装饰器则是将一般的变量变为了函数,这个函数在后期会运行

def mylog(func):
    print('logging')
    def wrapper():
        print('before')
        func()
        print('after')
    return main
def main1():
    print('i am main1')
mylog(main1)()
mylog(main1).__name__
'''
#print信息
logging
before
i am main1
after
logging
#output
'wrapper'
'''

3.简化

用@符号进行简化调用,简化后可以用于函数执行的性能测试

def mylog1(func):
    print('logging')
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper
@mylog1
def main1():
    print('i am another')
main1()
'''
#print信息
logging
before
i am another
after
'''

4.对装饰器进行改进

我们可以看到我们虽然做到了在函数前后打印或做其他东西,但是函数的主体已经变了,不是mylog了,变成原来最初返回的函数体了。但是到这里我们可以做一下简化了

def mylog2(func):
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper

@mylog2
def main2():
    print('i am in main2')
main2()
print(main2.__name__)
'''
#print信息
before
i am in main2
after
wrapper
'''

简化后运行更简单了,不用两个括号了,这就是@的用处。

5.保留初始函数信息

函数体(名字等属性)依然是最后返回的wrapper,需要继续改进。引入functool中的wrapper函数,对属性等信息进行复制保留。它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps

def mylog3(func):
    @wraps(func)
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper
@mylog3
def main3():
    print('i am in main3')
main3()
print(main3.__name__)
'''
#print信息
before
i am in main3
after
main3
'''

这里我们看到main3的函数不再被wrapper替换了

6.带参数的装饰器(3层)

有时候装饰器也需要带参数,这个时候为了要把参数传进去,需要在最外层套一层,用于传入装饰器的参数,这样就成了3层,再内一层才开始传入函数

#带参数的装饰器
def mylog5(logname = '1'):
    def logdeco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('before')
            print(logname)
            return func(*args, **kwargs)
        return wrapper
    return logdeco

@mylog5()
def main5():
    print('i am in ',main5.__name__)
main5()
'''
before
1
i am in  main5
'''
@mylog5(logname='2')
def main6():
    print('i am in ',main6.__name__)
main6()
print(main6.__name__)    
'''
before
2
i am in  main6
'''

7.装饰器类(配合call函数使用)

甚至可以用建立一个装饰器类如下,让call函数(定义在类上的call函数)多接受一个函数作为参数,就可以再@logit的时候,直接用作装饰器了

from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        print('notify1')
        pass

@logit()
def myfunc1():
    pass 
myfunc1()
'''
print信息
myfunc1 was called
notify1
'''

8.对类的继承和具化

作为类当然也可以继承,所以可以在这个基础上构造更具体的log函数,比如

class email_logit(logit):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 发送一封email到self.email,省略
        print('notify')
        pass
@email_logit()
def myfunc2():
    pass
myfunc2()
'''
print信息
myfunc2 was called
notify
'''

9.常用装饰器

@property,把类内的方法当作类属性使用,必须要保证类方法有返回值
@classmethod,类方法,不需要self参数,但第一个参数需要是表示自身类的cls参数
@staticmethod,静态方法,不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

#1.
class car():
    def __init__(self,name,price):
        self._name = name
        self._price = price
    @property
    def car_name(self):
        return self._name
    @car_name.setter
    def car_name(self,val):
        self._name = val
    def price(self):
        return self._price
benz = car('benz',30)
print(benz.car_name)
#output:benz
benz.car_name='bmw'
print(benz.car_name)
#output:bmw

#2.

#3.

10.装饰器的顺序

最上面的是最外层

@a
@b
@c
def main():
    pass
a(b(c(main())))

11.使用场景

打印日志,检查授权(如果有授权就执行,没有就不执行),性能测试,事务处理,缓存
这里贴一下缓存的代码

#装饰器实现缓存

def cache(func):
    cache = {}
    @wraps(func)
    def wrap(*args):
#        print(args)
        if args not in cache:
            cache[args] = func(*args)
            #print(cache)
        return cache[args]
    return wrap

class Solution:
    @cache
    def fib(self, N):
        if N < 2:
            return N
        else:
            return self.fib(N - 2) + self.fib(N - 1)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容