我的解决方案在问题底部,基于MisterMiyagi的示例。
我不确定标题应该如何最好地表达。我的想法如下。我有一个包含一些实现的抽象基类。其中一些实现在其逻辑中相互引用,简化如下:
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'
然而我不知道如何创建一个可以执行此操作的元类,我想听听元类是否是正确的选择,如果是,应该如何实现。
解决方案
参考MisterMiyagi的示例,我能得到一个类似于我的元类想法的行为。
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)) # <class 'abc.X2[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)) # <class '__main__.X2'>
X2
继承自X1
而不是实例化它(使用super().f() + 'X2'
)?这样你就可以简单地增强X1
,就这样。 - a_guestXi
实现之间的行为实际上是非常不同的,因此让它们从彼此继承而不是从基类继承是没有意义的。另外,正如我所说,我不想将这个新功能添加到X
家族中。这是我想要添加在其顶部作为Y
家族的新功能,同时保留原始的X
家族。我想这样做是为了分离功能,并且也有可能拥有一个Z
家族,它也建立在X
之上,但与Y
无关。 - user2124834class Y(X, family='Y')
。然后你可以在元类的__new__
中相应地调整变量。 - a_guestfamily
参数放在哪里,也不知道为什么'Y'
是一个字符串? - user2124834