【发布时间】:2019-02-28 23:56:35
【问题描述】:
我的解决方案在问题的底部,基于 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 转换为抽象元类的方法,这样它的实现需要一个“基础”参数才能成为类,然后可以对其进行实例化。然后在类中使用此参数来实例化具有正确基础的其他实现。
在基类中创建具有新功能的实例将如下所示:
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就可以了。 -
不同
Xi实现之间的行为在现实中是完全不同的,所以我认为让它们从彼此继承而不是从基类继承是没有意义的。另外,正如我所说,我不想将此新功能添加到X系列。这是我想在其之上添加的新功能,作为Y系列,同时保留原始X系列。我想这样做是为了分离功能,并有可能拥有一个Z系列,它也建立在X之上,但与Y无关。 -
那么一般来说如何使用元类,您可以将族作为参数传递,例如
class Y(X, family='Y')。然后你可以在元类__new__中相应地调整变量。 -
我认为这可能接近我的想法,但我不知道如何实现它。例如我不知道
family参数去哪里或者为什么'Y'是一个字符串? -
按照您的描述,不同的类层次结构之间存在一些重耦合。您确定要寻找专业,即
class Y2(Y, X2[Y1]),而不是装饰,即class Y2(Y(X2(Y1)))?
标签: python inheritance metaclass