美文网首页
24.python装饰器

24.python装饰器

作者: LIKESUNE | 来源:发表于2022-02-17 18:26 被阅读0次

python装饰器的本质是做装饰用的,比如你在手机外面贴了张膜,那这个膜作为装饰品,并不影响你原来的手机的使用方式,并且这个装饰品还给你增加了防摔的新特性。python装饰器亦是如此,它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。下面我们一步步来研究一下

def  hi():
     print("Hi")
hi() 
# output: hi

这是一个最简单的函数,我们通过hi()的方式,对函数进行了调用。其实在python中,万物皆可引用,如下:

def  hi():
     print("Hi")
hello = hi
hello()
# output:hi

这里我们只是把hi这个函数引用给到hello了,并没有立刻调用这个函数。引用就是这个函数的寻址方式。比如一个人的名字叫张三,你给重新起了一个名字叫张狗蛋,这两个名字我们都能找到这个人。那这个有什么用那?接着往下看。
有一天我需要用统计一下函数执行的日志信息,于是函数改成了:

def  hi():
    logging.info("%s is running" % hi.__name__)
    print("Hi")
hi()
#output: hi is running
#        Hi

问题得到了短暂的解决,但是随着代码越来越多,代码中又增加了hello函数和good函数。

def  hi():
    logging.info("%s is running" % func.__name__)
    print("Hi")

def  hello():
    print("Hello")

def  good():
    print("Good")
hi()

这几个函数都需要打印日志,于是你想到,定义一个日志打印函数,把这些函数当一个参数传入不就行了,于是:

def log(func):
    logging.warning("%s is running" % func.__name__)
    func()

def  hi():
    print("Hi")

def  hello():
    print("Hello")

def  good():
    print("Good")   

# 注意这里传入的是参数名,不是hi(),hi()的话函数就执行了
log(hi)
# output:Hi
# WARNING:root:hi is running
log(hello)
# output:Hello
# WARNING:root:hello is running
log(good)
# output:Good
# WARNING:root:good is running

这样似乎问题得到了解决,但是有一个问题就是函数的调用方式被我们改变了,如果这是一个大工程,那么把所有原来的函数调用方式都改一遍是我们不希望看到的。那么能不能在不改变原函数使用方式的情况下,完成我们的需求,当然可以,也就是使用我们的装饰器:

# 这是一个装饰器,返回的是一个函数wrapfunc的引用
def  decorator(func):
       def wrapfunc():
           logging.warning("%s is running" % func.__name__)
           func()
       return wrapfunc  # 注意这里返回的是引用,不要加括号

def  hi():
    print("Hi")

def  hello():
    print("Hello")

hi = decorator(hi)
hello = decorator(hello)
# 调用方式没有发生改变
hi()
hello()

# output:
#Hi
#Hello
#WARNING:root:hi is running
#WARNING:root:hello is running

为了偷懒,我们将hi=decorator(hi),hello =decorator(hello)简化为@函数名放在待修饰函数头上,如下:

# 这是一个装饰器,返回的是一个函数wrapfunc的引用
def  decorator(func):
       def wrapfunc():
           logging.warning("%s is running" % func.__name__)
           func()
       return wrapfunc  # 注意这里返回的是引用,不要加括号
@decorator
def  hi():
    print("Hi")

@ decorator
def  hello():
    print("Hello")

# 调用方式没有发生改变
hi()
hello()

也就是直接把装饰器的名字装饰到函数的头上去,这样一个简单的装饰器我们就完成了,啥?这个装饰器不够强大,不能传参,那简单:

# 这是一个装饰器,返回的是一个函数wrapfunc的引用
def out_func(leval):
    def  decorator(func):
           def wrapfunc():
               if leval == "warning":
                    logging.warning("%s is running" % func.__name__)
               func()
           return wrapfunc
    return decorator

@out_func(leval="warning")
def  hi():
    print("Hi")

@out_func(leval="info")
def  hello():
    print("Hello")

# 调用方式没有发生改变
hi()
hello()

# output:
#Hi
#Hello
#WARNING:root:hi is running

可以从输出的结果看到,我们通过对装饰器的参数传入,控制了我们是否要打印函数的日志信息。hi打印了,hello没有打印。

类装饰器

刚才我们的装饰器,是一个函数装饰器,那么当然也可以写一个类装饰器了。相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。

#我们写一个类用作装饰器
class decorator(object):
    def __init__(self,func):
        self._func = func

    def __call__(self, *args, **kwargs):
        logging.warning("%s is running" % self._func.__name__)
        self._func()
# 把这个类作为装饰器调用的时候还在方法的头上加上@类名这个其实就等价于  hi = decorator(hi)
@decorator
def  hi():
    print("Hi")

@decorator
def  hello():
    print("Hello")

# 调用方式没有发生改变
hi()
hello()

到这里我们讲了装饰器函数和装饰器类,但是使用装饰器有一个弊端就是会导致原信息的丢失,比如函数的docstring、name、参数列表,什么意思?

# 这是一个装饰器,返回的是一个函数wrapfunc的引用
def  decorator(func):
       def wrapfunc():
           logging.warning("%s is running" % func.__name__)
           func()
       return wrapfunc  # 注意这里返回的是引用,不要加括号
@decorator
def  hi():
    print("Hi")
print(hi)

# output:<function wrapfunc at 0x10d909398>

可以看到原本我们的函数名是hi,结果加了个装饰器后,这些信息变成了warpfunc的了,怎么办,我们可以在装饰器函数的头部使用functools.wraps装饰器来解决这个问题。

# coding=utf-8
import logging
import functools
# 这是一个装饰器,返回的是一个函数wrapfunc的引用
def  decorator(func):
        @functools.wraps(func)
        def wrapfunc():
           logging.warning("%s is running" % func.__name__)
           func()
        return wrapfunc  # 注意这里返回的是引用,不要加括号

def  hi():
    print("Hi")
hi = decorator(hi)
print hi
# output:<function hi at 0x10a4bf398>

相关文章

  • 24.python装饰器

    python装饰器的本质是做装饰用的,比如你在手机外面贴了张膜,那这个膜作为装饰品,并不影响你原来的手机的使用方式...

  • 装饰器

    """@装饰器- 普通装饰器- 带参数的装饰器- 通用装饰器- 装饰器装饰类- 内置装饰器- 缓存装饰器- 类实现...

  • typescript 五种装饰器

    装饰器类型 装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(fun...

  • python——装饰器详解

    一、装饰器概念 1、装饰器 装饰器:一种返回值也是一个函数的函数,即装饰器。 2、装饰器目的 装饰器的目的:装饰器...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • 装饰器

    装饰器 decorator类装饰器 带参数的装饰器 举例(装饰器函数;装饰器类;有参与无参) https://fo...

  • TypeScript装饰器

    前言 装饰器分类 类装饰器 属性装饰器 方法装饰器 参数装饰器需要在tsconfig.json中启用experim...

  • python之装饰器模版

    装饰器的作用:装饰器即可以装饰函数也可以装饰类。装饰器的原理:函数也是对象 1.定义装饰器 2.使用装饰器假设de...

  • 装饰器实验

    装饰器实验 说明 ts内包含了四个装饰器,类装饰器、属性装饰器、函数装饰器、参数装饰器,本文中测试一下其的使用。 ...

网友评论

      本文标题:24.python装饰器

      本文链接:https://www.haomeiwen.com/subject/owrslrtx.html