python装饰器详解&单例类装饰器实现

单例类装饰器的实现在文章最后面

1、不带参数的装饰器

我们用一个实际的例子来引入装饰器的概念,比如我们现在有一个方法Method(),然后我们需要在方法Method()执行之前在终端打印"Method is running",最简单的方法是在方法A()的开始部分加上一行代码:print("Method is running")

能不能不修改方法A()的代码实现这个功能呢?答案是肯定的,可以写一个方法decorator(),然后把A()作为入参,代码如下:

def decorator(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        print("Method is running")
        return fun()
    return wrapper

python中的方法可以作为参数传入另一个方法,所以当我们执行decorator(Method)的时候,返回值实际也是一个“方法”,这个“方法”会先执行print("Method is running")语句,然后再执行Method()。

这样修改后Method()代码确实没有变化,但是客户端的调用代码需要进行修改,之前调用方法Method (),现在是调用decorator(Method)(),为了不影响客户端调用代码,我们使用python的语法糖改造方法Method()的代码。

@decorator
def Method():
    # do something

实际上只是在Method()的前面加上一行@decorator,这样客户端代码中调用Method()实际上就相当于调用decorator(Method)()。

装饰器中的@functools.wraps(fun)可以保证返回的方法__name__属性和入参方法fun的__name__属性相同。

2、带入参的装饰器

上面不带参数的装饰器中,我们实现了在执行方法Method()之前打印"Method is running",如果我们想要打印自定义的字符串,就需要使用带参数的装饰器。

实际上就是在不带参数的装饰器上再封装一层即可,代码如下:

def decorator_par(name):
    def decorator(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):
            print("Method {} is running".format(name))
            return fun()
        return wrapper
    return decorator

在使用装饰器的时候设置参数name,实现打印不同的信息。完整的代码如下:

import functools

def decorator_par(name):
    def decorator(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):
            print("Method {} is running".format(name))
            return fun()
        return wrapper
    return decorator

@decorator_par("A")
def A():
    print("I am A")

@decorator_par("B")
def B():
    print("I am B")

A()
B()

输出为:

Method A is running
I am A
Method B is running
I am B

3、单例类装饰器的实现

在python中使用装饰器来实现一些重复性的工作很方便,例如下面实现的单例装饰器方法,在任何类前面加上@singleton就可以将该类修改成一个单例类,在开发具体的类的时候只需要关注具体类的功能即可,这就是装饰器的便捷之处。

def singleton(cls):
    def wrapper(*args, **kwargs):
        if not hasattr(cls, "__single_instance"):
            setattr(cls, "__single_instance", cls(*args, **kwargs))
            wrapper.clean = lambda : delattr(cls, "__single_instance")
        return getattr(cls, "__single_instance")
    return wrapper


@singleton
class Apple:
    def __init__(self, color, price):
        self.color = color
        self.price = price


apple1 = Apple("red", 1.2)
print(f"apple1 id: {id(apple1)}, color: {apple1.color}, price: {apple1.price}")

apple2 = Apple("green", 0.7)
print(f"apple2 id: {id(apple2)}, color: {apple2.color}, price: {apple2.price}")

Apple.clean()
apple3 = Apple("green", 1.5)
print(f"apple3 id: {id(apple3)}, color: {apple3.color}, price: {apple3.price}")

上面的代码输出如下(python3.6.6):

apple1 id: 2476531484320, color: red, price: 1.2
apple2 id: 2476531484320, color: red, price: 1.2
apple3 id: 2476531484432, color: green, price: 1.5

可以看到前两次实例化的apple1和apple2内存地址是相同的,说明Apple类被singleton装饰后实现了单例,然后调用Apple.clean()清除了缓存的单例对象之后,就可以重新生成一个新的对象apple3。

上面的实现使用动态绑定特性,python中的类实际上也是一个对象,因此可以动态的绑定属性和方法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值