首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python装饰器

Python装饰器

作者头像
zy010101
发布2022-05-05 16:00:32
发布2022-05-05 16:00:32
5350
举报
文章被收录于专栏:程序员程序员

装饰器模式

装饰器是一种设计模式,只不过在Python中有了语法层面的支持。

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的方法的一个包装,因此被称为装饰器。

装饰器这种设计模式比派生子类或者函数重载等方式更加灵活。 装饰器主要用于扩展一个类(方法)的功能,或者动态增加功能,动态撤销功能。因为装饰器和被装饰的对象之间没有耦合关系。

闭包

首先,我们需要学习闭包。那么什么是闭包? 闭包就是内层函数, 对外层函数(非全局)的变量的引用. 叫闭包。例如:

代码语言:javascript
复制
def fun1(x):
    "闭包"
    def fun2(y):
        print(x+y)
    return fun2

f = fun1(3)     # 给外层函数传入x=3,之后的f(1),f(2),f(3)调用的时候,内层函数就会引用外层函数的变量x的值3
f(1)
f(2)
f(3)

程序执行结果如下所示:

代码语言:javascript
复制
4
5
6

检测闭包

print(f.__closure__) 函数有一个__closure属性,用来检测闭包。如果该结果不是None,那么就是闭包,否则不是闭包。

装饰器

闭包返回了内层函数,而装饰器就是利用了闭包的特性。将被装饰的函数作为参数传入到闭包中,然后在闭包中对函数原来的功能可以做出更改。python提供了特殊的语法@装饰器放在函数外面即可。例如,我现在有个函数,如下所示:

代码语言:javascript
复制
def myPrint(name):
    print("Hello,"+name)

这时,我想给myPrint函数做一些功能上的扩展,例如,在打印Hello之前,先打印"------------------",在打印完Hello之后,也加上"------------------"。借助装饰器,我们可以这样实现该功能。

代码语言:javascript
复制
def outer(fun):
    def inner(name):
        print("------------------")
        fun(name)
        print("------------------")
    return inner

@outer
def myPrint(name):
    print("Hello,"+name)

myPrint("赵四")

在inner函数中,我们调用了myPrint函数。不过在调用前后都加上了一行print,然后在外层函数中返回内层函数。这样,我们就使用装饰器对myPrint完成了装饰,扩展了myPrint原有的功能。

解释一下@outer的语法到底做了什么?实际上,这相当于myPrint=outer(myPrint),现在,我们不使用python中的特殊语法,来看看效果。

代码语言:javascript
复制
def outer(fun):
    def inner(name):
        print("------------------")
        fun(name)
        print("------------------")
    return inner


def myPrint(name):
    print("Hello,"+name)

myPrint=outer(myPrint)
myPrint("赵四")

程序执行结果和使用@outer是一模一样的。现在,我们再来看一个例子来加深一下印象。

代码语言:javascript
复制
def outer(f):
    def inner(x, y):
        res = f(x, y) + 100
        return res
    return inner

@outer  # 相当于outer(func)
def func(x, y):
    return x + y

res = func(1, 2)
print(res)

这个例子中原本的func函数是将他们相加起来,但是经过装饰之后,值会增加100。另外需要注意的是,上一个例子中的inner函数中没有return语句,实际上,由于不知道原来的函数是否有返回值,通常应该写上return语句。因此,第二个例子是我们在编写装饰器的时候通常使用的模板。

最后,还有一地方可以优化,那就是刚才的装饰器装饰的函数的参数是固定的,现在我们继续进程改造,让其可以装饰任意的函数。

代码语言:javascript
复制
def outer(f):
    def inner(*args, **kwargs):
        print("开始装饰...")
        res = f(*args, **kwargs)
        print("装饰结束")
        return res
    return inner


@outer
def fun1(name):
    print(name)

@outer
def fun2(x, y):
    return x + y

print(fun1("赵四"))
print("------------------------分割线----------------------")
print(fun2(1, 1))

只需要给装饰器的内层函数设置*args,**kwargs参数以及将这两个参数传入外层函数的参数中即可。

带参数的装饰器

上面的例子都是没有参数的装饰器,装饰器本身也是可以有参数的。例如:

代码语言:javascript
复制
def outer(x):
    def outwrapper(fun):
        def wrapper(*args, **kwargs):
            print(F"—————————{x}——————————")
            res = fun(*args, **kwargs)
            print(F"—————————{x}——————————")
            return res
        return wrapper
    return outwrapper


@outer(123)     # 相当于myPrint = outer(123)(myPrint)
def myPrint(name, age):
    print("name:", name)
    print("age:", age)

myPrint("赵四", 18)

需要注意,带参数的装饰器@outer(123)实际上相当于myPrint = outer(123)(myPrint), 因此需要三层函数嵌套才可以。这样,装饰器中可以传入参数,先形成一个完整的装饰器,然后再来装饰函数。

装饰器类

装饰器也可以是一个类。这个方式实际上是运算符重载带来的效果,使用__call__来重载调用表达式()既可以实现装饰器类。例如:

代码语言:javascript
复制
class Outer():
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("_________________________")
        res = self.func(*args, **kwargs)
        print("_________________________")
        return res
@Outer
def hello(a, b, c):
    print(a, b, c)

hello("hello,","good","morning")

同样,也是可以带上参数的装饰器类,这比起带参数的装饰器函数更加简单。

代码语言:javascript
复制
class Outer():
    def __init__(self, x):
        self.x = x
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f"___________{self.x}___________")
            res = func(*args, **kwargs)
            print(f"___________{self.x}___________")
            return res
        return wrapper

@Outer(666)
def hello(a, b, c):
    print(a, b, c)

hello('111', '222', '333')
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 装饰器模式
  • 闭包
    • 检测闭包
    • 装饰器
  • 带参数的装饰器
  • 装饰器类
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档