以与 Python 2.7 和 Python 3.5 兼容的方式使用 abc.ABCMeta

新手上路,请多包涵

我想创建一个类 abc.ABCMeta 作为元类并且与 Python 2.7 和 Python 3.5 兼容。直到现在,我只在 2.7 或 3.5 上成功地做到了这一点——但从来没有同时在两个版本上做到过。有人可以帮我吗?

蟒蛇2.7:

 import abc
class SomeAbstractClass(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def do_something(self):
        pass

Python 3.5:

 import abc
class SomeAbstractClass(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def do_something(self):
        pass

测试

如果我们使用合适版本的 Python 解释器运行以下测试(Python 2.7 -> 示例 1,Python 3.5 -> 示例 2),它在两种情况下都会成功:

 import unittest
class SomeAbstractClassTestCase(unittest.TestCase):
    def test_do_something_raises_exception(self):
        with self.assertRaises(TypeError) as error:
            processor = SomeAbstractClass()
        msg = str(error.exception)
        expected_msg = "Can't instantiate abstract class SomeAbstractClass with abstract methods do_something"
        self.assertEqual(msg, expected_msg)

问题

使用 Python 3.5 运行测试时,预期的行为不会发生( TypeError 在实例化时不会引发 SomeAbstractClass ):

 ======================================================================
FAIL: test_do_something_raises_exception (__main__.SomeAbstractClassTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/tati/sample_abc.py", line 22, in test_do_something_raises_exception
    processor = SomeAbstractClass()
AssertionError: TypeError not raised

----------------------------------------------------------------------

而使用 Python 2.7 运行测试会引发 SyntaxError

  Python 2.7 incompatible
 Raises exception:
  File "/home/tati/sample_abc.py", line 24
    class SomeAbstractClass(metaclass=abc.ABCMeta):
                                     ^
 SyntaxError: invalid syntax

原文由 Tatiana Al-Chueyr 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 879
2 个回答

您可以使用 six.add_metaclasssix.with_metaclass

 import abc, six

@six.add_metaclass(abc.ABCMeta)
class SomeAbstractClass():
    @abc.abstractmethod
    def do_something(self):
        pass

six 是一个 Python 2 和 3 兼容库。您可以通过运行 pip install six 或通过将最新版本的 six.py 下载到您的项目目录来安装它。

对于那些更喜欢 future 而不是 six 的人,相关功能是 future.utils.with_metaclass

原文由 vaultah 发布,翻译遵循 CC BY-SA 4.0 许可协议

以与 Python 2.7 和 Python 3.5 兼容的方式使用 abc.ABCMeta

如果我们只使用 Python 3(这是 3.4 中的新 功能),我们可以这样做:

 from abc import ABC

并继承自 ABC 而不是 object 。那是:

 class SomeAbstractClass(ABC):
    ...etc

你仍然不需要额外的依赖(第六模块)——你可以使用元类来创建父类(这基本上就是第六模块在 with_metaclass 中所做的):

 import abc

# compatible with Python 2 *and* 3:
ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()})

class SomeAbstractClass(ABC):

    @abc.abstractmethod
    def do_something(self):
        pass

或者您可以就地进行(但这更混乱,并且对重用的贡献不大):

 # use ABCMeta compatible with Python 2 *and* 3
class SomeAbstractClass(abc.ABCMeta('ABC', (object,), {'__slots__': ()})):

    @abc.abstractmethod
    def do_something(self):
        pass

请注意,签名看起来比 six.with_metaclass 有点混乱,但它的语义基本相同,没有额外的依赖。

两种解决方案

现在,当我们尝试在不实现抽象的情况下进行实例化时,我们得到的正是我们所期望的:

 >>> SomeAbstractClass()
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    SomeAbstractClass()
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract methods do_something

注意 __slots__ = ()

我们 刚刚将空的 __slots__ 添加 到 Python 3 标准库中的 ABC 便利类,我的答案已更新以包含它。

没有 __dict____weakref__ABC 父允许用户拒绝他们的创建,除非你使用 __slots__ 在儿童课程中,并且依赖于隐式 __dict__ ABC __weakref__

快速修复是在您的子类中适当地声明 __dict____weakref__ 。更好的(对于 __dict__ )可能是明确声明所有成员。

原文由 Russia Must Remove Putin 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题