【问题标题】:Difference between abstract class and interface in PythonPython中抽象类和接口的区别
【发布时间】:2010-09-27 04:30:22
【问题描述】:

Python中的抽象类和接口有什么区别?

【问题讨论】:

    标签: python interface abstract-class


    【解决方案1】:

    您有时会看到以下内容:

    class Abstract1:
        """Some description that tells you it's abstract,
        often listing the methods you're expected to supply."""
    
        def aMethod(self):
            raise NotImplementedError("Should have implemented this")
    

    因为 Python 没有(也不需要)正式的接口契约,所以不存在抽象和接口之间的 Java 风格区别。如果有人努力定义一个正式的接口,它也将是一个抽象类。唯一的区别在于文档字符串中声明的意图。

    当你有鸭子打字时,抽象和接口之间的区别是一件令人毛骨悚然的事情。

    Java 使用接口是因为它没有多重继承。

    因为Python有多重继承,你也可能会看到这样的东西

    class SomeAbstraction:
        pass  # lots of stuff - but missing something
    
    class Mixin1:
        def something(self):
            pass  # one implementation
    
    class Mixin2:
        def something(self):
            pass  # another
    
    class Concrete1(SomeAbstraction, Mixin1):
        pass
    
    class Concrete2(SomeAbstraction, Mixin2):
        pass
    

    这使用一种带有混合的抽象超类来创建不相交的具体子类。

    【讨论】:

    • S. Lott,你的意思是因为鸭子类型,has-a(接口)和is-a(继承)之间的区别并不重要吗?
    • abstract 和 interface 之间的区别是当你有鸭子打字的时候,这是一件令人毛骨悚然的事情。我不知道“实质性”是什么意思。从设计的角度来看,它是“真实的”——它有实质内容。但从语言的角度来看,可能没有支持。您可以采用约定来区分 Python 中的抽象类和接口类定义。
    • @L.DeLeo - 你确定你的 has-a 与 is-a 的概念是正确的吗?我通常将区别视为 has-a = 成员变量与 is-a = 继承(父类 接口)。在 Java 中思考 Comparable 或 List;这些都是 is-a 关系,无论它们是接口还是抽象类。
    • NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__)) 是更多信息错误消息:)
    • @Lorenzo has-a 关系与继承、鸭子类型、接口和抽象类没有任何关系(这四个都指 is-a 关系)。
    【解决方案2】:

    Python中抽象类和接口有什么区别?

    对于一个对象来说,接口是该对象上的一组方法和属性。

    在 Python 中,我们可以使用抽象基类来定义和实施接口。

    使用抽象基类

    例如,假设我们要使用来自collections 模块的抽象基类之一:

    import collections
    class MySet(collections.Set):
        pass
    

    如果我们尝试使用它,我们会得到一个TypeError,因为我们创建的类不支持集合的预期行为:

    >>> MySet()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Can't instantiate abstract class MySet with abstract methods
    __contains__, __iter__, __len__
    

    所以我们需要至少实现__contains____iter____len__。让我们使用documentation中的这个实现示例:

    class ListBasedSet(collections.Set):
        """Alternate set implementation favoring space over speed
        and not requiring the set elements to be hashable. 
        """
        def __init__(self, iterable):
            self.elements = lst = []
            for value in iterable:
                if value not in lst:
                    lst.append(value)
        def __iter__(self):
            return iter(self.elements)
        def __contains__(self, value):
            return value in self.elements
        def __len__(self):
            return len(self.elements)
    
    s1 = ListBasedSet('abcdef')
    s2 = ListBasedSet('defghi')
    overlap = s1 & s2
    

    实现:创建抽象基类

    我们可以通过将元类设置为abc.ABCMeta 并在相关方法上使用abc.abstractmethod 装饰器来创建自己的抽象基类。元类会将修饰函数添加到__abstractmethods__ 属性,在定义这些函数之前阻止实例化。

    import abc
    

    例如,“effable”被定义为可以用文字表达的东西。假设我们想在 Python 2 中定义一个可实现的抽象基类:

    class Effable(object):
        __metaclass__ = abc.ABCMeta
        @abc.abstractmethod
        def __str__(self):
            raise NotImplementedError('users must define __str__ to use this base class')
    

    或者在 Python 3 中,元类声明略有变化:

    class Effable(object, metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def __str__(self):
            raise NotImplementedError('users must define __str__ to use this base class')
    

    现在如果我们尝试在不实现接口的情况下创建一个 effable 对象:

    class MyEffable(Effable): 
        pass
    

    并尝试实例化它:

    >>> MyEffable()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
    

    我们被告知我们还没有完成这项工作。

    现在,如果我们通过提供预期的接口来遵守:

    class MyEffable(Effable): 
        def __str__(self):
            return 'expressable!'
    

    然后我们就可以使用派生自抽象类的具体版本:

    >>> me = MyEffable()
    >>> print(me)
    expressable!
    

    我们可以用这个做其他事情,比如注册已经实现这些接口的虚拟子类,但我认为这超出了这个问题的范围。但是,此处演示的其他方法必须使用 abc 模块来调整此方法。

    结论

    我们已经证明,抽象基类的创建定义了 Python 中自定义对象的接口。

    【讨论】:

      【解决方案3】:

      Python >= 2.6 有Abstract Base Classes

      抽象基类(缩写 ABCs)通过以下方式补充鸭子打字 提供一种定义接口的方法 当其他技术如 hasattr() 会很笨拙。 Python自带 许多用于数据结构的内置 ABC (在集合模块中),数字 (在数字模块中)和流 (在 io 模块中)。你可以创建 您自己的 ABC 与 abc 模块。

      还有Zope Interface 模块,它被zope 之外的项目使用,比如twisted。我不太熟悉它,但有一个 wiki 页面 here 可能会有所帮助。

      一般来说,您不需要抽象类的概念,或者 python 中的接口(已编辑 - 有关详细信息,请参阅 S.Lott 的答案)。

      【讨论】:

      • 在 Python 中使用 ABC 可以获得什么?
      【解决方案4】:

      用更基本的方式来解释: 界面有点像一个空的松饼盘。 它是一个类文件,其中包含一组没有代码的方法定义。

      抽象类是一样的,但并不是所有的函数都需要为空。有些可以有代码。它不是严格为空的。

      为什么要区分: Python 没有太大的实际差异,但在大型项目的规划层面上,谈论接口可能更常见,因为没有代码。尤其是当您与熟悉该术语的 Java 程序员一起工作时。

      【讨论】:

      • +1 表示 ABC 可以拥有自己的实现 - 这似乎是一种非常酷的智取自己的方式
      【解决方案5】:

      Python 并没有这两个概念。

      它使用duck typing,从而消除了对接口的需求(至少对于计算机而言:-))

      Python

      Python >= 2.6: 抽象基类做exist (http://docs.python.org/library/abc.html)。并允许您指定必须在子类中实现的方法。我不太喜欢语法,但功能就在那里。大多数时候,从“使用”客户端使用鸭子打字可能会更好。

      【讨论】:

      • Python 3.0 确实添加了真正的抽象基类。它们用于集合模块以及其他地方。 docs.python.org/3.0/library/abc.html
      • 关于为什么鸭式打字消除了对接口的需求的参考会很有帮助。对我来说,鸭子类型(我理解为“戳”任何对象上的任何方法或属性的能力)似乎并不明显,这意味着您不需要指定所需的行为(并让编译器提醒您来实现它们),这就是我对抽象基类的理解。
      • 与其说是鸭子类型,不如说是对多重继承的支持消除了接口和抽象类之间的人为线,例如Java 画好了。
      【解决方案6】:

      通常,接口仅用于使用单继承类模型的语言。在这些单继承语言中,如果任何类可以使用特定方法或方法集,则通常使用接口。同样在这些单继承语言中,抽象类用于在没有或更多方法之外定义类变量,或者利用单继承模型来限制可以使用一组方法的类的范围。

      支持多重继承模型的语言倾向于只使用类或抽象基类而不是接口。由于 Python 支持多重继承,它不使用接口,您可能希望使用基类或抽象基类。

      http://docs.python.org/library/abc.html

      【讨论】:

        【解决方案7】:

        抽象类是包含一个或多个抽象方法的类。除了抽象方法,抽象类还可以有静态、类和实例方法。 但是在接口的情况下,它只有抽象方法,没有其他方法。因此,继承抽象类不是强制性的,但继承接口是强制性的。

        【讨论】:

          【解决方案8】:

          为了完整起见,我们应该提到PEP3119 其中 ABC 被引入并与接口进行比较, 和原始Talin's 评论。

          抽象类不是完美的接口:

          • 属于继承层次结构
          • 是可变的

          但如果你考虑用自己的方式编写它:

          def some_function(self):
               raise NotImplementedError()
          
          interface = type(
              'your_interface', (object,),
              {'extra_func': some_function,
               '__slots__': ['extra_func', ...]
               ...
               '__instancecheck__': your_instance_checker,
               '__subclasscheck__': your_subclass_checker
               ...
              }
          )
          
          ok, rather as a class
          or as a metaclass
          and fighting with python to achieve the immutable object
          and doing refactoring
          ...
          

          你很快就会意识到你正在发明轮子 最终达到 abc.ABCMeta

          abc.ABCMeta 被提议作为缺少的界面功能的有用补充, 这对于像 python 这样的语言来说已经足够公平了。

          当然,在编写第 3 版时,它能够得到更好的增强,并添加了新的语法和不可变接口概念......

          结论:

          The abc.ABCMeta IS "pythonic" interface in python
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-03-02
            • 1970-01-01
            • 1970-01-01
            • 2010-12-27
            • 2016-11-28
            • 2018-10-11
            • 2010-12-12
            相关资源
            最近更新 更多