【发布时间】:2018-11-28 13:58:27
【问题描述】:
注意:我问的不是常见的python装饰器,而是装饰器设计模式。
我想编写一个能够修改具体组件调用的函数的装饰器,以下代码示例说明了我的问题:
In [2]: class Animal:
...: def sound(self):
...: raise NotImplementedError
...:
...: def speak(self):
...: print(self.sound())
...:
In [3]: class Dog(Animal):
...: def sound(self):
...: return 'woof!'
...:
In [4]: class Bigfy:
...: def __init__(self, animal):
...: self.animal = animal
...:
...: def sound(self):
...: return self.animal.sound().upper()
...:
...: def speak(self):
...: return self.animal.speak()
...:
In [5]: dog = Dog()
...: dog.speak()
...:
woof!
In [6]: big_dog = Bigfy(Dog())
...: big_dog.sound()
...:
Out[6]: 'WOOF!'
In [7]: big_dog.speak()
woof! # I want 'WOOF!' here
我要增强功能的方法是sound,但是这个方法不是客户端直接调用的,而是speak内部调用的,所以sound上的所有换行都不起作用。
是否有可能使用装饰器设计模式来实现我想要的?如果不是,我应该看看什么设计模式?
编辑:感谢大家的快速回答,按照@FHTMitchell 的模式,我找到了以下解决方案:
In [1]: import inspect
In [2]: class Animal:
...: def sound(self):
...: raise NotImplementedError
...:
...: def speak(self):
...: print(self.sound())
...:
...: # Key change
...: @property
...: def unwrapped(self):
...: return self
...:
In [3]: class Dog(Animal):
...: def sound(self):
...: return 'woof!'
...:
In [4]: class BaseWrapper:
...: def __new__(cls, animal, **kwargs):
...: self = super().__new__(cls)
...: self.__init__(animal, **kwargs)
...:
...: # Automatically points unwrapped methods to last wrapper
...: for attr in dir(animal):
...: # Don't get magic methods
...: if attr.startswith('__') or attr.startswith('old'):
...: continue
...:
...: value = getattr(animal, attr)
...: if inspect.ismethod(value):
...: # Store old method
...: setattr(self, 'old_' + attr, value)
...: # Points to new method
...: setattr(animal.unwrapped, attr, getattr(self, attr))
...:
...: return self
...:
...: def __init__(self, animal):
...: self.animal = animal
...:
...: # Delegate all non-implemented attrs calls to wrapped class
...: def __getattr__(self, name):
...: return getattr(self.animal, name)
...:
...: # Helps with editor auto-completion
...: def __dir__(self):
...: dir_list = super().__dir__()
...: dir_list.extend(self.animal.__dir__())
...:
...: return dir_list
...:
In [5]: class Bigify(BaseWrapper):
...: def sound(self):
...: return self.old_sound().upper()
...:
In [6]: class Angrify(BaseWrapper):
...: def sound(self):
...: return self.old_sound() + '!!!'
...:
In [7]: class Happify(BaseWrapper):
...: def sound(self):
...: return self.old_sound() + ' =)'
...:
In [8]: big_angry_dog = Happify(Angrify(Bigify(Dog())))
...: big_angry_dog.speak()
...:
WOOF!!!! =)
【问题讨论】:
-
这听起来更适合子类化。 (请注意,Python 中的装饰器意味着不同的东西;最好将其称为包装器。)
-
@DanielRoseman,我想在运行时添加功能,所以没有子类化 =/
-
这是同样的情况吗? reclassing an instance
-
@Quazar 这是python,一切都发生在运行时,甚至是类定义。你到底什么意思?您只能访问实例?
-
@FHTMitchell,我的意思是我希望用户能够根据需要添加新功能,例如,他们先造一只狗,然后把它变大,然后把它变成棕色,等等。 .. 同样,我对实现此功能的其他设计模式持开放态度。
标签: python oop design-patterns decorator