背景信息
我有一个具有相对复杂的类层次结构的python应用程序。它需要与python 2.6到python 3.5一起工作(我知道范围很大!),而且我在使用abc时遇到了一些特殊的问题。我正在使用six
库的with_metaclass
来减轻一些伤害,但它仍然有问题。
一组特殊的课程一直给我带来麻烦。下面是它的简化形式:
from abc import ABCMeta
from six import with_metaclass
# SomeParentABC is another ABC, in case it is relevant
class MyABC(with_metaclass(ABCMeta, SomeParentABC)):
def __init__(self, important_attr):
self.important_attr = important_attr
def gamma(self):
self.important_attr += ' gamma'
class MyChild1(MyABC):
def __repr__(self):
return "MyChild1(imporant_attr=%s)" % important_attr
def alpha(self):
self.important_attr += ' alpha'
class MyChild2(MyABC):
def __repr__(self):
return "MyChild2(imporant_attr=%s)" % important_attr
def beta(self):
self.important_attr += ' beta'
在MyABC
中捆绑了大量的gamma
类函数,以及alpha
、beta
等少数子类专用函数。我希望MyABC
的所有子类都继承相同的__init__
和gamma
属性,然后堆叠自己的特定特征。
问题是,为了让MyChild1
和MyChild2
共享__init__
的代码,MyABC
需要有一个具体的初始化式。在Python 3中,一切都工作得很好,但在Python 2中,当初始化器是具体的时,我在实例化MyABC
时无法获得TypeErrors
。
我的测试套件中有一个片段,看起来像这样
def test_MyABC_really_is_abstract():
try:
MyABC('attr value')
# ideally more sophistication here to get the right kind of TypeError,
# but I've been lazy for now
except TypeError:
pass
else:
assert False
不知何故,在Python 2.7(我假设是2.6,但没有费心检查)中,此测试失败。
MyABC
没有任何其他抽象属性,但是在没有alpha
或beta
的情况下实例化具有gamma
的类是没有意义的。现在,我已经通过在MyChild1
和MyChild2
中复制__init__
函数来通过DRY违规,但随着时间的推移,这变得越来越繁重。
我如何给Python 2 ABC一个具体的初始化器而不使其可实例化,同时保持Python 3的兼容性?换句话说,我想尝试在Python 2和Python 3中实例化MyABC
以抛出TypeError
s,但它只在Python 3中抛出它们。
with_metaclass
我相信在这里看到with_metaclass
的代码是相关的。这是根据six
项目的现有许可和版权提供的,(c) 2010-2014
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with
# the actual metaclass.
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
标题>six.with_metaclass()
元类可能与abc不兼容,因为它覆盖了type.__new__
;这可能会干扰对具体方法进行正常测试的程序。
尝试使用@six.add_metaclass()
类装饰器:
从abc导入ABCMetaimport add_metaclass
@add_metaclass(ABCMeta)
class MyABC(SomeParentABC):
def __init__(self, important_attr):
self.important_attr = important_attr
def gamma(self):
self.important_attr += ' gamma'
演示:>>> from abc import ABCMeta, abstractmethod
>>> from six import add_metaclass
>>> @add_metaclass(ABCMeta)
... class MyABC(object):
... @abstractmethod
... def gamma(self): pass
...
>>> MyABC()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyABC with abstract methods gamma
请注意,您确实需要有抽象方法而不需要具体实现来引发TypeError
!