假设我使用的是一个库,当调用其函数时,该库为我提供了该库中定义的类的实例:
>>> from library import find_objects
>>> result = find_objects("name = any")
[SomeObject(name="foo"), SomeObject(name="bar")]
让我们进一步假设我想将新属性附加到这些实例。例如,一个分类器以避免每次我想对实例进行分类时都运行此代码:
>>> from library import find_objects
>>> result = find_objects("name = any")
>>> for row in result:
... row.item_class= my_classifier(row)
请注意,这是人为设计的,但可以说明问题:我现在有类SomeObject
的实例,但是属性item_class
未在该类中定义,并触发了类型检查器。
所以当我现在写:
print(result[0].item_class)
输入错误。由于编辑者不知道该属性的存在,还会触发编辑器中的自动完成。
而且,更不用说这种实现方式非常丑陋和笨拙。
我可以做的一件事是创建SomeObject
的子类:
class ExtendedObject(SomeObject):
item_class = None
def classify(self):
cls = do_something_with(self)
self.item_class = cls
这现在使所有内容都明确,我有机会正确记录新属性并提供适当的类型提示。一切都干净。但是,如前所述,实际实例是在 library 内部创建的,我无法控制实例化。
旁注:我在flask
类的Response
中遇到了这个问题。我注意到flask
实际上提供了一种使用Flask.response_class
自定义实例化的方法。但是我仍然很感兴趣如何在不提供这种注射接缝的图书馆中实现这一目标。
我可以做的一件事就是写一个包装程序,做这样的事情:
class WrappedObject(SomeObject):
item_class = None
wrapped = None
@staticmethod
def from_original(wrapped):
self.wrapped = wrapped
self.item_class = do_something_with(wrapped)
def __getattribute__(self, key):
return getattr(self.wrapped, key)
但这似乎很hacky,无法在其他编程语言中使用。
或尝试复制数据:
from copy import deepcopy
class CopiedObject(SomeObject):
item_class = None
@staticmethod
def from_original(wrapped):
for key, value in vars(wrapped):
setattr(self, key, deepcopy(value))
self.item_class = do_something_with(wrapped)
但是这同样令人讨厌,并且在对象起诉属性和/或描述符时存在风险。
是否存在类似这种已知的“干净”模式?
答案 0 :(得分:0)
我会采用您的WrappedObject
方法的一种变体,并进行以下调整:
SomeObject
:在这种情况下,合成比继承更合适from_original
是不必要的:您可以使用适当的__init__
方法item_class
应该是实例变量,而不是类变量。应该在您的WrappedObject
类构造函数中初始化__getattribute__
并将所有内容转发到包装的对象之前,请三思。如果您只需要原始SomeObject
类的一些方法和属性,则最好将它们显式地实现为方法和属性class WrappedObject:
def __init__(self, wrapped):
self.wrapped = wrapped
self.item_class = do_something_with(wrapped)
def a_method(self):
return self.wrapped.a_method()
@property
def a_property(self):
return self.wrapped.a_property