基于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
创建。为了让我的想法奏效,这必须成为
Y1
在里面
Y2.__init__
. 当然不是这样的:
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'
然而,我不知道如何创建一个这样做的元类。我想知道一个元类是否是正确的想法,如果是这样的话,如何去做。
解决方案
用Mistermyagi的例子,我可以得到一些我认为有用的东西。这种行为接近我的元类思想。
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)) #