【发布时间】:2014-08-09 17:11:56
【问题描述】:
背景
我有一个具有相对复杂的类层次结构的 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 没有任何其他抽象属性,但是实例化一个具有gamma 而没有alpha 或beta 的类是没有意义的。
目前,我通过在 MyChild1 和 MyChild2 中复制 __init__ 函数来解决 DRY 违规问题,但随着时间的推移,这变得越来越繁重。
如何在不使其可实例化的情况下为 Python 2 ABC 提供具体的初始化程序,同时保持 Python 3 的兼容性?
换句话说,我想在 Python 2 和 Python 3 中尝试实例化 MyABC 以抛出 TypeErrors,但它只在 Python 3 中抛出它们。
with_metaclass
我相信在这里查看with_metaclass 的代码是相关的。
这是根据six 项目的现有许可和版权提供的,(c) 2010-2014 Bejamin Peterson
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', (), {})
【问题讨论】:
-
在 Python 2 中抛出
TypeError时的完整回溯是什么? -
@MartijnPieters 我不确定你在这里要求什么。 Python2 中没有抛出
TypeError。这正是问题所在:我想要一个被抛出,因为MyABC是抽象的。 -
对,当时还不清楚;您希望它会被抛出,但事实并非如此。
-
由
type.__new__处理;我在这里看不到__new__的任何覆盖;SomeParentABC可能会这样做吗? -
@MartijnPieters 我刚刚编辑了问题以包含
with_metaclass代码,它确实编辑了__new__。我承认这让我读起来头疼。
标签: python python-2.7 python-3.x unit-testing abc