关于python:一详解Python装饰器真香

8次阅读

共计 4604 个字符,预计需要花费 12 分钟才能阅读完成。

明天写篇文章,详解 Python 装璜器。文末有我的微信二维码,打算建个读者群,想交换 Python 的敌人能够加一下。

装璜器 (decorator) 是一种高级 Python 语法,也是 Python 函数式编程的一部分。写法是 @xxx,在 Pandas、Django 框架源码里常常能见到,利用十分宽泛。

尽管不必装璜器也不影响开发,但用了装璜器能够让你的代码“秀”起来,当然装璜器的作用不仅仅是“秀”,更重要的是它能够让你的代码更简洁,可读性更高;业务逻辑解耦,保护更容易。

1 函数根底

学习装璜器之前,咱们先学习下 Python 函数的两个个性,函数体内能够定义函数  和  函数能够被援用。有根底的敌人能够略过,间接看下一大节。

例 1.  函数体内定义函数

def func1():
    def func2():
        return "in func2"

    res = func2()

    return res

在 func1 函数体内,定义了一个 func2 函数并调用,而后返回调用的后果。其实很多动态编程语言,如 C、Java,不反对在函数体内定义函数。但在 Python 中是反对的。

例 2. 函数能够被援用

new_func = func1
print(new_func())

func1 是个函数,定义跟 例 1 是一样的。能够看到函数名能够像变量那样应用。赋值后,new_func 就援用了 func1,就能够间接通过 new_func() 形式来调用函数。\
既然函数名能够向变量一样赋值,那天然就能够被 return 返回。

def func1():
    def func2():
        return "in func2"

    print(func2)
    return func2


new_func = func1()
print(new_func)
print(new_func())

输入:

<function func1.<locals>.func2 at 0x102499b80>
<function func1.<locals>.func2 at 0x102499b80>
in func2

print(func1) 和 print(new_func) 输入后果一样,阐明他们是同一个函数。\

2 装璜器

有了下面的根底,再学习装璜器就很容易了。上面通过一个“炒土豆丝”的例子来学习装璜器。

例 3.“炒土豆丝”根底版

def tu_dou_si():
    """" 清炒土豆丝" 流程
    """print(' 荡涤土豆 ')
    print('去皮,切丝')
    print('清炒')
    print('出锅')

这是最根底的版本。为了跟国内接轨,例子中应用中文拼音定义函数名或变量名。

上面咱们革新 例 3,将其变成装璜器版本。

例 4.“炒土豆丝”装璜器版

def my_decorator(func):
    def wrapper():
        func()
        print('清炒')
        print('出锅')

    return wrapper


def tu_dou_si():
    print('荡涤土豆')
    print('去皮,切丝')

有没有发现它跟 例 2 特地像。

my_decorator 函数就是一个装璜器,它参数 func 示意能够传入一个函数,它外面定义了一个函数 wrapper,而后将 wrapper 函数返回。

wrapper 的中文含意是包裹、包装的意思,所以在这里示意对 func() 进行了包装,包装的成果就是多了两个 print 输入。

tu_dou_si 函数,只保留跟土豆丝相干的 print 语句。

定义好了装璜器,就能够依照上面的形式,实现 例 3 中“清炒土豆丝”的流程

qing_chao_tu_dou_si = my_decorator(tu_dou_si)
qing_chao_tu_dou_si()

输入:

荡涤土豆
去皮,切丝
清炒
出锅

对于下面的代码 Python 有一种更简洁的形式来实现 \

@my_decorator
def tu_dou_si():
    print('荡涤土豆')
    print('去皮,切丝')tu_dou_si()

就是 Python 的装璜器。因为 tu_dou_si 函数曾经被 my_decorator 装璜过了,所以,间接调用 tu_dou_si 函数即可实现“清炒土豆丝”的流程。

3 装璜器的劣势

尽管咱们曾经实现了一个装璜器,但你心里可能会有疑难,装璜器写法不简略,代码量比 例 3 也不少,它的劣势到底在哪。

从“清炒土豆丝”这个单点的确看不出装璜器劣势,如果从线再到面角度来看,就能看出装璜器的劣势了。

“清炒土豆丝”流程能够拆分为两个业务,一个是筹备食材业务,即:print(‘ 荡涤土豆 ’)  和 print(‘ 去皮,切丝 ’);另一个是制作食材的业务,即:print(‘ 清炒 ’)  和 print(‘ 出锅 ’)。之所以这样拆,是因为这两局部是互相独立的子业务,并且能不便看到装璜器的劣势。问题 1 . 要再实现一个“清炒山药”的流程,怎么做

@my_decorator
def shan_yao():
    print('荡涤山药')
    print('去皮,切丝')

能够看到,用装璜器来实现能够复用 my_decorator 中制作食材的业务过程,减少代码复用性,使整体代码更简洁。

问题 2 . 想把“清炒土豆丝”改成“凉拌土豆丝”,怎么办

def my_decorator(func):
    def wrapper():

        func()
        print('凉拌')
        print('出锅')

    return wrapper

间接批改 my_decorator 业务就能够了,不会对其余业务产生副作用。

了解了下面两个问题,咱们也就晓得了装璜器的作用。如果不应用装璜器(如:例 3)要么会产生代码冗余,要么就业务耦合在一起改一个业务影响到另外的业务。

4 装璜器的进阶

接下来,来看看装璜器高级一些的用法。

例 5.  装璜器的参数

def cai_factory(cai_type='清炒'):
    def my_decorator(func):
        def wrapper():
            func()
            print(f'{cai_type}')
            print('出锅')

        return wrapper
    return my_decorator


@cai_factory('凉拌')
def tu_dou_si():
    print('荡涤土豆')
    print('去皮,切丝')

定义了一个 cai_factory 函数(即:菜工厂),它外面蕴含了之前定义的 my_decorator 装璜器。cai_factory 函数接管 cai_type 参数用来指定如果制作食材。

定义 tu_dou_si 函数时,应用新的装璜器,并且传入参数即可。

例 6. 被装璜函数的参数

def cai_factory(cai_type='清炒'):
    def my_decorator(func):
        def wrapper(cai_name):
            func(cai_name)
            print(f'{cai_type}')
            print('出锅')

        return wrapper
    return my_decorator


@cai_factory()
def cai(cai_name='土豆'):
    print(f'荡涤{cai_name}')
    print('去皮,切丝')

定义一个用 cai_factory 润饰的函数 cai,接管 cai_name 参数来指定制作的食材。同时,在 cai_factory 装璜器中的 wrapper 函数中减少 cai_name 参数,并在调用 func 时,传入该参数。

例 7. 被装璜函数的不定参数

def cai_factory(cai_type='清炒'):
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
            print(f'{cai_type}')
            print('出锅')

        return wrapper
    return my_decorator


@cai_factory()
def cai(cai_name, cai_cnt):
    print(f'荡涤{cai_name} {cai_cnt}')
    print('去皮,切丝')

cai('山药', '两个')

例 6 只能反对 1 个参数,本例中的 wrapper 函数的参数改为 args 和 *kwargs,就能够反对任意参数,把装璜器变得更通用。

5 装璜类

类装璜器的用法跟函数是一样的。这次咱们不做菜了,开始玩游戏了。游戏外面皮肤和英雄的关系就完满地体现了装璜类与装璜类的关系。上面咱们就定义一个英雄类,而后定义一个皮肤类装璜它。

def pi_fu(cls):
    class PiFuClass:
        def __init__(self, name, pi_fu_name):
            self.wrapped = cls(name, pi_fu_name)
            self.pi_fu_name = pi_fu_name

        def display(self):
            self.wrapped.display()
            print(f'展现皮肤{self.pi_fu_name}')

    return PiFuClass


@pi_fu
class YingXiong:
    def __init__(self, name, pi_fu_name):
        self.name = name
        self.pi_fu_name = pi_fu_name

    def display(self):
        print(f'展现英雄{self.name}')


ya_se = YingXiong('亚瑟', '死亡骑士')
ya_se.display()

写法和用法跟函数装璜器相似,这里就不再赘述了。

6 内置装璜器

Python 内置了一些装璜器,这里列几个咱们常常会用到的一些:

  • @property:润饰类办法,使得咱们能够像拜访属性一样来获取一个函数的返回值
  • @staticmethod:润饰类办法,示意该办法是一个静态方法,能够间接被调用无需实例化
  • @wraps:函数来进行装璜,能够让咱们在装璜器外面拜访在装璜之前的函数的属性

简略总结一下,Python 装璜器反对润饰办法和类,可能减少被润饰办法和办法的性能。同时又能起到业务之间解耦、减少代码复用性和简化代码的作用。

以上就是本次分享的所有内容,如果你感觉文章还不错,欢送关注公众号:Python 编程学习圈,每日干货分享,发送“J”还可支付大量学习材料。或是返回编程学习网,理解更多编程技术常识。

正文完
 0