Python基础-面向对象编程之装饰器

Python面向对象编程之装饰器

一、什么是装饰器

函数、方法或类定义的前面可以使用一个特殊的符号,称为装饰器,其目的是修改定义后面的行为。装饰器使用 @ 符号表示,必须放在单独的行上并且位于对应的函数、方法或类之前,例如:

class Foo():
    @staticmethod
    def bar():
        pass

也可以使用多个装饰器,但每个装饰器必须位于不同的行上,例如:

@foo
@bar
def spam():
    pass

二、函数装饰器

装饰器本质上是一个函数,其主要用途是包装另一个函数或者类。这种包装的首要目的是透明地修改或增强被包装对象的行为。表示装饰器的语法是特殊符号@。 如下所示:

@trace
def square(x):
  return x*x

上面的代码可以等效为:

def square(x):
  return x*x
square = trace(square)

上面的例子中定义了函数square(), 但是在定义后,函数对象本身就立即被传递给函数trace(),后者返回一个对象替代原始的square。现在,让我们考虑trace的实现,从而说明这样做的好处:

enable_tracing = True
if enable_tracing:
    debug_log = open('debug.log', 'w')
    
def trace(func):
    if enable_tracing:
        def callf(*args, **kwargs):
            debug_log.write(f'calling {func.__name__} : {args}, {kwargs}')
            r = func(*args, **kwargs)
            debug_log.write(f'{func.__name__} returned {r}')
            return r
    else:
        return func

这段代码中,trace()创建了写有一些调试输出的包装器函数,然后调用了原始的函数对象。因此如果调用square()函数,将会看到包装器函数中write方法的输出。trace()函数返回的函数callf是一个闭包,用于替换原始的函数。另外,trace功能本身只能像上面这样使用全局的enable_tracing来启用,如果把enable_tracing设为False,那么trace()装饰器只是返回未修改的原始函数。因此,禁用trace时,使用装饰器不会增加性能上的负担。

使用装饰器时,它们必须出现在函数或类定义的上方,且位于单独的行上。可以同时使用多个装饰器,当一个函数被多个装饰器装饰时,装饰器的加载顺序是从下往上的(从内到外的)。例如:

@foo
@bar
@spam
def grok(x):
  pass

在这个例子中,装饰器将按照它们出现的先后顺序应用,结果是:

def grok(x):
  pass

grok = foo(bar(spam(grok)))

装饰器也可以接收参数, 例如:

@eventhandler('BUTTON')
def handle_button(msg):
  pass

@eventhandler('RESET')
def handle_reset(msg)
  pass

如果提供参数,装饰器的语义如下所示:

def handle_button(msg):
  pass

temp = eventhandler('BUTTON')       # 使用提供的参数调用装饰器
handle_button = temp(handle_button) # 使用装饰器返回的函数

事件处理程序装饰器示意:

event_handlers = {}
def eventhandler(event):
  def register_function(f):
    event_handlers[event] = f
    return f
  return register_function

三、类装饰器

有时候,我们需要在定义类之后执行一些额外处理,例如将类添加到注册表或者数据库。这类问题的一个解决方案就是使用类装饰器。类装饰器是一种函数, 它接受类作为输入并返回类作为输出。

对于类装饰器,应该让装饰器函数始终返回类对象作为结果。

看如下的例子:

registry = {}
def register(cls):
  registry[cls.__clsid__] = cls
  return cls

在这个例子中,注册函数在类中查找__clsid__属性。如果找到,则使用该属性将类添加到字典中,将类标识符映射到类对象。要使用该函数,可以在类定义前将它作为装饰器,例如:

@register
class Foo():
  __clsid__ = '123-456'
  def bar(self):
    pass

可以看到,使用装饰器语法可以带来极大的便利。

当然了,你也可以通过另一种方式来做到:

class Foo():
  __clsid__ = '123-456'
  def bar(self):
    pass

register(Foo)

尽管可以在类装饰器函数中对类做很多神奇的事情,但是最好避免执行过于花哨的操作,例如为类添加一个包装器或者重写类内容。


小手一抖,点个赞再走哦~~~

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

smart_cat

你的鼓励将是我写作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值