假设我有一个第三方库,其中元类要求我实现某些东西。但我希望有一个中级"抽象"没有的子类。我怎么能这样做?
将此视为第三方库具有的最小示例:
class ServingMeta(type):
def __new__(cls, name, bases, classdict):
if any(isinstance(b, ServingMeta) for b in bases):
if "name" not in classdict:
# Actual code fails for a different reason,
# but the logic is the same.
raise TypeError(f"Class '{name}' has no 'name' attribute")
return super().__new__(cls, name, bases, classdict)
class Serving(object, metaclass=ServingMeta):
def shout_name(self):
return self.name.upper()
我无法修改上面的代码。它是一个外部依赖(我不想分叉)。
代码应该以这种方式使用:
class Spam(Serving):
name = "SPAM"
spam = Spam()
print(spam.shout_name())
然而,我碰巧有很多垃圾邮件,我想介绍一个带有常见帮助方法的基类。像这样:
class Spam(Serving):
def thrice(self):
return " ".join([self.shout_name()] * 3)
class LovelySpam(Spam):
name = "lovely spam"
class WonderfulSpam(Spam):
name = "wonderful spam"
显然,这并没有奏效,并且因为预期的TypeError: Class 'SpamBase' has no 'name' attribute declared
而失败。第三方图书馆是否有一个没有元类的SpamBase
类,我可以将其分类 - 但这次没有这样的运气(我已经向图书馆作者提到了这种不便)。
我可以把它变成混音:
class SpamMixin(object):
def thrice(self):
return " ".join([self.shout_name()] * 3)
class LovelySpam(SpamMixin, Serving):
name = "lovely spam"
class WonderfulSpam(SpamMixin, Serving):
name = "wonderful spam"
然而,这让我和我的IDE有点畏缩,因为在任何地方重复SpamMixin
很快变得很麻烦,而且因为object
没有shout_name
属性(我不会&#39} ; t想要沉默分析工具)。简而言之,我不喜欢这种方法。
我还能做些什么?
有没有办法获得无元类版本的Serving
?我想到这样的事情:
ServingBase = remove_metaclass(Serving)
class Spam(ServingBase, metaclass=ServingMeta):
...
但是不知道如何实际实施remove_metaclass
以及它何时合理可能(当然,它必须是可行的,有一些内省,但它可能需要更多的奥术魔法而不是我可以演员。)
欢迎任何其他建议。基本上,我想让我的代码DRY(一个基类来统治它们),并让我的linter / code分析图标全部为绿色。
答案 0 :(得分:1)
确实没有直接从Python类中删除元类的方法,因为元类创建了该类。你可以尝试的是使用不同的元类重新创建类,这不会有不需要的行为。例如,您可以使用type
(默认元类)。
In [6]: class Serving(metaclass=ServingMeta):
...: def shout_name(self):
...: return self.name.upper()
...:
In [7]: ServingBase = type('ServingBase', Serving.__bases__, dict(vars(Serving)))
基本上这需要__bases__
元组和Serving
类的命名空间,并使用它们来创建新类ServingBase
。注:这意味着ServingBase
将从Serving
接收所有基础和方法/属性,其中一些可能已由ServingMeta
添加。