可以将 self.__class__ 设置为其他内容吗?

问题描述 投票:0回答:1

向一组兄弟实例的任何成员添加方法的正确方法是什么(即每个实例的类都继承自一个公共基类)?

兄弟姐妹来自

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__
    或其他机制来钩住结构。”

我想不出如何在实现上述断言目标的同时完成其中任何一个。谢谢你给我指路!

python
1个回答
0
投票

您可以为给定的项目和 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

演示:https://ideone.com/JVRzyO

© www.soinside.com 2019 - 2024. All rights reserved.