python:类与装饰器
- 2019 年 11 月 21 日
- 筆記
简单装饰
在python中,装饰器可以是一个类。就是这么任性。 要把一个类做为装饰器是很简单的。这里借鉴一个很有意思的代码,python管道语法糖的实现,作者JulienPalard 项目
class Pipe: def __init__(self, function): self.function = function functools.update_wrapper(self, function) def __ror__(self, other): return self.function(other) def __call__(self, *args, **kwargs): return Pipe(lambda x: self.function(x, *args, **kwargs))
可以这么用:
@Pipe def average(iterable): """ Build the average for the given iterable, starting with 0.0 as seed Will try a division by 0 if the iterable is empty... """ total = 0.0 qte = 0 for x in iterable: total += x qte += 1 return total / qte [1, 3, 6] | average
神奇啊,神奇啊。我一看这份代码,就爱上了这个简洁的小项目。真是太有BG了。 但是,这个项目有一个小问题。那就是,它不能装饰类方法。
装饰类方法
类的函数和普通函数非常相似,在调用的时候会以self关键字传入当前实例作为参数。这是大家都明白的。 但值得一提的是,类函数有比较复杂的调用机制,当执行 cls.method的一瞬间,系统会调用该方法(注意不是类)的__get__(self, instance, cls)
函数。instance就是所属类的实例,cls是类的签名。 所以当用上面Pipe类直接装饰一个类方法,将会丢失类的self关键字。要实现功能,必须获取运行时的instance实例,要获取instance,就必须hack__get__
函数。 所以,代码变化成了下面这样:
import functools class Pipe: def __init__(self, function): self.function = function functools.update_wrapper(self, function) def __ror__(self, other): return self.function(other) def __get__(self, instance, cls): return Pipe(lambda x: self.function(instance, x))
要点如下: 1. 在get中是一个经过实例包装的pipe类实例。这个实例包装了instance。 2. 这个成员必须是Pipe类型,否则,普通的Lambda方法或者函数,没有实现xor,自然也不能和|
连用。
这个类装饰器,也兼容普通的函数,因为普通函数不会触发__get__
方法。所以,行为与原项目一致。