向一组兄弟实例的任何成员添加方法的正确方法是什么(即每个实例的类都继承自一个公共基类)?
兄弟姐妹来自
fsspec
,不受我控制,但就是这样。
class Base:
def open(self): pass
def read(self): pass
class A(Base):
def read(self): pass
class B(Base):
def read(self): pass
可能会有一个
C
,某些插件可能会添加一个D
......这些都是我无法控制的。我所有的代码获取的是 Base
的某个子类的实例。
我的代码获取一个实例,需要将其传递给另一个需要继承自
Base
的类的实例的库。但我希望这个实例有一个新功能(不是在我的代码之外具有通用功能的功能,因此无需更改 fsspec
)。在代码中,类似这样。
with fsspec.open(...) as f: # f is an instance of `A`, `B`, etc.
g = dunno_what(f) # asking about this part
dataset = xarray.open_dataset(g) # handing off to xarray
现在
xarray
的行为会有所不同,具体取决于 g
实现的内容,并且要求 g
是 Base
的实例。
在我看来,我的功能将作为 mixin 的一种方法出现。
class Mixin:
def feature(self): pass
那么以下似乎可以实现我的目标,但有很多缺点。
for Item in (A, B):
# `item` is the `f` in the minimal example above
item = Item()
# here's what I've tried, but is advised against
item.__class__ = type("Item", (item.__class__, Mixin), {})
# it works though ...
assert isinstance(item, Base)
assert item.open.__func__ is Base.open
assert item.read.__func__ is Item.read
assert item.feature.__func__ is Mixin.feature
分配给
item.__class__
不适合新手,所以我对abarnert给出的建议替代方案感兴趣(第三个不在我的控制范围内):
__new__
或其他机制来钩住结构。”我想不出如何在实现上述断言目标的同时完成其中任何一个。谢谢你给我指路!
您可以为给定的项目和 mixin 类创建一个代理对象,以
Base
作为基类,这样代理对象会将属性查找委托给项目,然后委托给 mixin 类作为后备:
def add_mixin(item, mixin):
class _Proxy(Base):
def __getattribute__(self, name):
try:
return getattr(item, name)
except AttributeError:
return getattr(mixin, name).__get__(item)
return _Proxy()
for Item in (A, B):
item = Item()
item = add_mixin(item, Mixin)
assert isinstance(item, Base)
assert item.open.__func__ is Base.open
assert item.read.__func__ is Item.read
assert item.feature.__func__ is Mixin.feature