根据宫城先生的例子,我的解决方案位于问题的底部
我不确定如何最好地表达标题.我的想法如下.我有一些实现的抽象基类.其中一些实现将彼此称为逻辑的一部分,简化如下:
import abc
# the abstract class
class X(abc.ABC):
@abc.abstractmethod
def f(self):
raise NotImplementedError()
# first implementation
class X1(X):
def f(self):
return 'X1'
# second implementation, using instance of first implementation
class X2(X):
def __init__(self):
self.x1 = X1()
def f(self):
return self.x1.f() + 'X2'
# demonstration
x = X2()
print(x.f()) # X1X2
print(x.x1.f()) # X1
现在,我想在其他模块中使用这些类.但是,我想向层次结构中的所有类添加一些额外的功能(例如,功能g).我可以通过将其添加到基类X中来做到这一点,但是我想保留单独定义的功能.例如,我可能想像这样定义新功能:
class Y(X):
def g(self):
return self.f() + 'Y1'
这将创建具有新功能的另一个基类,但是当然不会将其添加到现有的实现X1和X2中.我必须使用菱形继承来做到这一点:
class Y1(X1, Y):
pass
class Y2(X2, Y):
pass
# now I can do this:
y = Y2()
print(y.g()) # X1X2Y1
上面的方法可以正常工作,但是仍然存在问题.在X2 .__ init__中,创建X1的实例.为了使我的想法可行,必须在Y2 .__ init__中将其变为Y1.但是,事实并非如此:
print(y.x1.g()) # AttributeError: 'X1' object has no attribute 'g'
我认为我可能正在寻找一种将X转换为抽象元类的方法,以便其实现需要“ base”参数成为类,然后可以将其实例化.然后,在类中使用此参数来实例化具有正确基数的其他实现.
在基类中创建具有新功能的实例将如下所示:
class Y:
def g(self):
return self.f() + 'Y1'
X2(Y)()
这将导致一个对象等同于以下类的实例:
class X2_with_Y:
def __init__(self):
self.x1 = X1(Y)()
def f(self):
return self.x1.f() + 'X2'
def g(self):
return self.f() + 'Y1'
但是我不知道如何创建一个可以做到这一点的元类.我想听听一个元类是否是正确的主意,如果是的话,该怎么做.
解
使用宫城老师的例子,我得到了一些我认为可行的东西.该行为接近于我的元类想法.
import abc
class X(abc.ABC):
base = object # default base class
@classmethod
def __class_getitem__(cls, base):
if cls.base == base:
# makes normal use of class possible
return cls
else:
# construct a new type using the given base class and also remember the attribute for future instantiations
name = f'{cls.__name__}[{base.__name__}]'
return type(name, (base, cls), {'base': base})
@abc.abstractmethod
def f(self):
raise NotImplementedError()
class X1(X):
def f(self):
return 'X1'
class X2(X):
def __init__(self):
# use the attribute here to get the right base for the other subclass
self.x1 = X1[self.base]()
def f(self):
return self.x1.f() + 'X2'
# just the wanted new functionality
class Y(X):
def g(self):
return self.f() + 'Y1'
用法是这样的:
# demonstration
y = X2[Y]()
print(y.g()) # X1X2Y1
print(y.x1.g()) # X1Y1
print(type(y)) #
# little peeve here: why is it not '__main__.X2[Y]'?
# the existing functionality also still works
x = X2()
print(x.f()) # X1X2
print(x.x1.f()) # X1
print(type(x)) #