【Pyhont笔记】装饰器总结

知识点:python中函数也是对象,可以传递和当做参数的。

装饰器可看成是一个复杂语法的缩写:

@a

def b

       ...

等价于

b=a(b)

 

1.最基本的装饰器:参数为函数,返回函数的函数,可作为修饰器

def decorator(in_func):#函数做参数
    def out_func(a,b,c):
        print("输出点啥")
        return in_func(a,b,c)
    return out_func#返回函数,参数需要与被装饰函数(use_func)参数一致,或者用*args,**kwargs
    
#使用

@decorator
def use_func(a,b,c):
    ...

#等价于
use_func=decorator(use_func)

问题1:名称信息丢失

print(use_func.__name__)#被装饰函数名称
# Output: wrapTheFunction

解决方法:

from functools import wraps
 
def decorator(in_func):
    @wraps(in_func)
    ...
    return out_func#返回函数


print(use_func.__name__)#被装饰函数名称
# Output: use_func

注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

 

问题2:use_func的参数如何获得的

use_func的参数是如何获得的:

我们可以在定义 out_func 函数的时候指定参数:
def out_func(a,b,c):
        logging.warn("%s is running" % in_func.__name__)
        return in_func(a,b,c)
return out_func

这样 use_func 函数定义的参数就可以定义在 out_func函数中。use_func如果有很多个参数。当装饰器不知道 use_func 到底有多少个参数时,我们可以用 *args 来代替:

def out_func(*args):
        logging.warn("%s is running" % in_func.__name__)
        return in_func(*args)
return out_func

对于use_func 函数还定义了的关键字参数比如:

def use_func(name, age=None, height=None):
    ...

这时,可以把 out_func 函数指定关键字函数:

def out_func(*args, **kwargs):
        # args是一个数组,kwargs一个字典
        logging.warn("%s is running" % in_func.__name__)
        return in_func(*args, **kwargs)
return out_func

 

2.带参数的装饰器:

例1:

装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 use_func 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。

def use_logging(level):
    def decorator(in_func):
        def out_func(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % in_func.__name__)
            elif level == "info":
                logging.info("%s is running" % in_func.__name__)
            return in_func(*args)
        return out_func

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()

上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中

@use_logging(level="warn")等价于@decorator

例2:

from functools import wraps
 
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
 
@logit()
def myfunc1():
    pass
 
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
 
@logit(logfile='func2.log')
def myfunc2():
    pass
 
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

3.装饰器类:

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

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)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '\n')
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        pass

这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

@logit()
def myfunc1():
    pass

现在,我们给 logit 创建子类,来添加 email 的功能(虽然 email 这个话题不会在这里展开)。

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
        # 这里就不做实现了
        pass

从现在起,@email_logit 将会和 @logit 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

参考自:

1:https://www.runoob.com/w3cnote/python-func-decorators.html\

2:https://foofish.net/python-decorator.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值