标签归档:abstract-class

如何在python抽象类中创建抽象属性

问题:如何在python抽象类中创建抽象属性

在以下代码中,我创建了一个基本抽象类Base。我希望所有从其继承的类都Base提供该name属性,因此我将该属性设置为@abstractmethod

然后,我创建了一个Base名为的子类,该子类Base_1旨在提供一些功能,但仍保持抽象。中没有name属性Base_1,但是python实例化了该类的对象而没有错误。一个人如何创建抽象属性?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  

In the following code, I create a base abstract class Base. I want all the classes that inherit from Base to provide the name property, so I made this property an @abstractmethod.

Then I created a subclass of Base, called Base_1, which is meant to supply some functionality, but still remain abstract. There is no name property in Base_1, but nevertheless python instatinates an object of that class without an error. How does one create abstract properties?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  

回答 0

Python 3.3开始,修复了一个错误,这意味着property()装饰器现在应用于抽象方法时,可以正确地标识为抽象。

注:订单的问题,你必须使用@property@abstractmethod

Python 3.3以上版本:python docs):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2:python docs

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...

Since Python 3.3 a bug was fixed meaning the property() decorator is now correctly identified as abstract when applied to an abstract method.

Note: Order matters, you have to use @property before @abstractmethod

Python 3.3+: (python docs):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2: (python docs)

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...

回答 1

Python 3.3之前,您不能嵌套@abstractmethod@property

使用@abstractproperty创建抽象属性(文档)。

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

该代码现在引发正确的异常:

追溯(最近一次通话):
  在第36行的文件“ foo.py”中 
    b1 = Base_1('abc')  
TypeError:无法使用抽象方法名称实例化抽象类Base_1

Until Python 3.3, you cannot nest @abstractmethod and @property.

Use @abstractproperty to create abstract properties (docs).

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

The code now raises the correct exception:

Traceback (most recent call last):
  File "foo.py", line 36, in 
    b1 = Base_1('abc')  
TypeError: Can't instantiate abstract class Base_1 with abstract methods name

回答 2

根据上面的詹姆斯回答

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

并将其用作装饰器

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()

Based on James answer above

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

and use it as a decorator

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()

`staticmethod`和`abc.abstractmethod`:会融合吗?

问题:`staticmethod`和`abc.abstractmethod`:会融合吗?

在我的Python应用程序中,我想创建一个既是staticmethod和又是的方法abc.abstractmethod。我该怎么做呢?

我尝试同时应用两个装饰器,但不起作用。如果我这样做:

import abc

class C(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    @staticmethod    
    def my_function(): pass

我遇到异常*,如果执行此操作:

class C(object):
    __metaclass__ = abc.ABCMeta

    @staticmethod    
    @abc.abstractmethod
    def my_function(): pass

没有强制执行抽象方法。

如何制作抽象的静态方法?

*exceptions:

File "c:\Python26\Lib\abc.py", line 29, in abstractmethod
 funcobj.__isabstractmethod__ = True
AttributeError: 'staticmethod' object has no attribute '__isabstractmethod__'

In my Python app I want to make a method that is both a staticmethod and an abc.abstractmethod. How do I do this?

I tried applying both decorators, but it doesn’t work. If I do this:

import abc

class C(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    @staticmethod    
    def my_function(): pass

I get an exception*, and if I do this:

class C(object):
    __metaclass__ = abc.ABCMeta

    @staticmethod    
    @abc.abstractmethod
    def my_function(): pass

The abstract method is not enforced.

How can I make an abstract static method?

*The exception:

File "c:\Python26\Lib\abc.py", line 29, in abstractmethod
 funcobj.__isabstractmethod__ = True
AttributeError: 'staticmethod' object has no attribute '__isabstractmethod__'

回答 0

class abstractstatic(staticmethod):
    __slots__ = ()
    def __init__(self, function):
        super(abstractstatic, self).__init__(function)
        function.__isabstractmethod__ = True
    __isabstractmethod__ = True

class A(object):
    __metaclass__ = abc.ABCMeta
    @abstractstatic
    def test():
        print 5
class abstractstatic(staticmethod):
    __slots__ = ()
    def __init__(self, function):
        super(abstractstatic, self).__init__(function)
        function.__isabstractmethod__ = True
    __isabstractmethod__ = True

class A(object):
    __metaclass__ = abc.ABCMeta
    @abstractstatic
    def test():
        print 5

回答 1

与开始的Python 3.3,它是可以组合 @staticmethod@abstractmethod,所以没有其他的建议是必要的了:

@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):

Starting with Python 3.3, it is possible to combine @staticmethod and @abstractmethod, so none of the other suggestions are necessary anymore:

@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):

回答 2

这样做:

  >>> import abc
  >>> abstractstaticmethod = abc.abstractmethod
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abstractstaticmethod
  ...     def themethod():
  ...          pass
  ... 
  >>> a = A()
  >>> Traceback (most recent call last):
  File "asm.py", line 16, in <module>
    a = A()
  TypeError: Can't instantiate abstract class A with abstract methods test

您输入“嗯?它只是重命名了@abstractmethod”,这是完全正确的。因为上述任何子类都必须包含@staticmethod装饰器。除了阅读代码时作为文档外,这里您不需要它。子类必须看起来像这样:

  >>> class B(A):
  ...     @staticmethod
  ...     def themethod():
  ...         print "Do whatevs"

要具有可以强制您将此方法设置为静态方法的函数,您必须将ABCmeta子类化以对其进行检查并强制执行。没有真正的回报,这是很多工作。(如果有人忘记了@staticmethod装饰器,无论如何他们都会得到一个明确的错误,只是不会提及静态方法。

因此,实际上它也一样有效:

  >>> import abc
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abc.abstractmethod
  ...     def themethod():
  ...         """Subclasses must implement this as a @staticmethod"""
  ...          pass

更新-另一种解释方式:

方法是静态的,它控制着它的调用方式。从不调用抽象方法。因此,除了出于文档目的之外,抽象静态方法是一个毫无意义的概念。

This will do it:

  >>> import abc
  >>> abstractstaticmethod = abc.abstractmethod
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abstractstaticmethod
  ...     def themethod():
  ...          pass
  ... 
  >>> a = A()
  >>> Traceback (most recent call last):
  File "asm.py", line 16, in <module>
    a = A()
  TypeError: Can't instantiate abstract class A with abstract methods test

You go “Eh? It just renames @abstractmethod”, and this is completely correct. Because any subclass of the above will have to include the @staticmethod decorator anyway. You have no need of it here, except as documentation when reading the code. A subclass would have to look like this:

  >>> class B(A):
  ...     @staticmethod
  ...     def themethod():
  ...         print "Do whatevs"

To have a function that would enforce you to make this method a static method you would have to subclass ABCmeta to check for that and enforce it. That’s a lot of work for no real return. (If somebody forgets the @staticmethod decorator they will get a clear error anyway, it just won’t mention static methods.

So in fact this works just as well:

  >>> import abc
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abc.abstractmethod
  ...     def themethod():
  ...         """Subclasses must implement this as a @staticmethod"""
  ...          pass

Update – Another way to explain it:

That a method is static controls how it is called. An abstract method is never called. And abstract static method is therefore a pretty pointless concept, except for documentation purposes.


回答 3

目前在Python 2.X中这是不可能的,Python 2.X仅将方法强制为抽象或静态方法,而不会两者都强制执行。

在Python 3.2+中,添加了新的装饰器abc.abstractclassmethodabc.abstractstaticmethod以将其强制实施抽象和静态或抽象与类方法结合起来。

参见Python Issue 5867

This is currently not possible in Python 2.X, which will only enforce the method to be abstract or static, but not both.

In Python 3.2+, the new decoratorsabc.abstractclassmethod and abc.abstractstaticmethod were added to combine their enforcement of being abstract and static or abstract and a class method.

See Python Issue 5867


为什么在Python中使用抽象基类?

问题:为什么在Python中使用抽象基类?

因为我习惯了Python中鸭子输入的旧方法,所以我无法理解对ABC(抽象基类)的需求。的帮助下是如何使用它们好。

我试图阅读PEP中的基本原理,但它使我感到头疼。如果我正在寻找一个可变序列容器,我会检查__setitem__,或更可能尝试使用它(EAFP)。我还没有真正使用过数字模块,它确实使用了ABC,但这是我必须了解的最接近的数字。

有人可以向我解释理由吗?

Because I am used to the old ways of duck typing in Python, I fail to understand the need for ABC (abstract base classes). The help is good on how to use them.

I tried to read the rationale in the PEP, but it went over my head. If I was looking for a mutable sequence container, I would check for __setitem__, or more likely try to use it (EAFP). I haven’t come across a real life use for the numbers module, which does use ABCs, but that is the closest I have to understanding.

Can anyone explain the rationale to me, please?


回答 0

精简版

ABC在客户端和已实现的类之间提供了更高级别的语义协定。

长版

类与其调用者之间存在合同。该类承​​诺做某些事情并具有某些属性。

合同有不同的级别。

在非常低的级别上,合同可能包含方法名称或其参数数量。

在静态类型的语言中,该约定实际上将由编译器强制执行。在Python中,您可以使用EAFP或键入内省以确认未知对象是否符合此预期合同。

但是合同中还有更高层次的语义承诺。

例如,如果有__str__()方法,则期望返回对象的字符串表示形式。它可以删除对象的所有内容,提交事务并在打印机上吐出空白页…但是,Python手册中对此有一个普遍的了解。

这是一种特殊情况,其中在手册中描述了语义约定。该print()方法应该做什么?它应该将对象写入打印机还是将行写入屏幕,或者其他?这取决于-您需要阅读评论以了解此处的完整合同。一段简单地检查该print()方法是否存在的客户代码已确认了合同的一部分-可以进行方法调用,但未就该调用的较高层语义达成协议。

定义抽象基类(ABC)是在类实现者和调用者之间产生合同的一种方式。它不仅是方法名称的列表,而且是对这些方法应该做什么的共识。如果您从该ABC继承,则承诺遵守注释中描述的所有规则,包括print()方法的语义。

与静态类型相比,Python的鸭子类型在灵活性方面具有许多优势,但是并不能解决所有问题。ABC在Python的自由形式和静态类型的语言的约束与约束之间提供了一种中间解决方案。

Short version

ABCs offer a higher level of semantic contract between clients and the implemented classes.

Long version

There is a contract between a class and its callers. The class promises to do certain things and have certain properties.

There are different levels to the contract.

At a very low level, the contract might include the name of a method or its number of parameters.

In a staticly-typed language, that contract would actually be enforced by the compiler. In Python, you can use EAFP or type introspection to confirm that the unknown object meets this expected contract.

But there are also higher-level, semantic promises in the contract.

For example, if there is a __str__() method, it is expected to return a string representation of the object. It could delete all contents of the object, commit the transaction and spit a blank page out of the printer… but there is a common understanding of what it should do, described in the Python manual.

That’s a special case, where the semantic contract is described in the manual. What should the print() method do? Should it write the object to a printer or a line to the screen, or something else? It depends – you need to read the comments to understand the full contract here. A piece of client code that simply checks that the print() method exists has confirmed part of the contract – that a method call can be made, but not that there is agreement on the higher level semantics of the call.

Defining an Abstract Base Class (ABC) is a way of producing a contract between the class implementers and the callers. It isn’t just a list of method names, but a shared understanding of what those methods should do. If you inherit from this ABC, you are promising to follow all the rules described in the comments, including the semantics of the print() method.

Python’s duck-typing has many advantages in flexibility over static-typing, but it doesn’t solve all the problems. ABCs offer an intermediate solution between the free-form of Python and the bondage-and-discipline of a staticly-typed language.


回答 1

@Oddthinking的答案是正确的,但我认为它没有想到在鸭蛋式的世界中Python具有ABC 的真实实际原因。

抽象方法很简洁,但我认为它们并没有真正填补鸭子类型尚未涵盖的任何用例。抽象基类的真正力量在于它们允许您自定义isinstanceand 行为的方式issubclass。(__subclasshook__基本上,它是基于Python __instancecheck____subclasscheck__ hook 的更友好的API 。)使内置结构适应于自定义类型,这是Python理念的很大一部分。

Python的源代码是示例性的。collections.Container在标准库中的定义方式(撰写本文时):

class Container(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if any("__contains__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

这个的定义__subclasshook__说,任何具有__contains__属性的类都被视为Container的子类,即使它没有直接对其进行子类化。所以我可以这样写:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

换句话说,如果实现正确的接口,那么您就是一个子类!ABC提供了一种正式的方式来定义Python中的接口,同时忠实于鸭子式输入的精神。此外,这以尊重开放式原则的方式工作

Python的对象模型从表面上看起来类似于更“传统”的OO系统(我的意思是Java *)的模型-我们得到了yer类,yer对象,yer方法-但是当您从头开始时,就会发现更丰富的东西并且更灵活。同样,Python的抽象基类概念对于Java开发人员来说可能是可识别的,但实际上它们的目的是非常不同的。

有时我发现自己编写了可以作用于单个项目或项目集合的多态函数,而且isinstance(x, collections.Iterable)hasattr(x, '__iter__')同等的代码try...except块更具可读性。(如果您不了解Python,那么这三个代码中哪一个最清楚?)

就是说,我发现我几乎不需要编写自己的ABC,而且通常我会通过重构发现需要一个ABC。如果我看到一个多态函数进行了大量的属性检查,或者许多函数进行了相同的属性检查,那么这种气味表明存在等待提取的ABC。

*无需参数Java是否是“传统的” OO系统…


附录:即使抽象基类可以覆盖行为isinstance,并issubclass,它仍然没有进入MRO虚拟子类。这对于客户端来说是一个潜在的陷阱:并非每个为其isinstance(x, MyABC) == True定义方法的对象MyABC

class MyABC(metaclass=abc.ABCMeta):
    def abc_method(self):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return True

class C(object):
    pass

# typical client code
c = C()
if isinstance(c, MyABC):  # will be true
    c.abc_method()  # raises AttributeError

不幸的是,这些“只是不这样做”陷阱(Python相对来说很少!)陷阱:避免同时使用a __subclasshook__和非抽象方法来定义ABC 。此外,您应该使定义__subclasshook__与ABC定义的一组抽象方法一致。

@Oddthinking’s answer is not wrong, but I think it misses the real, practical reason Python has ABCs in a world of duck-typing.

Abstract methods are neat, but in my opinion they don’t really fill any use-cases not already covered by duck typing. Abstract base classes’ real power lies in the way they allow you to customise the behaviour of isinstance and issubclass. (__subclasshook__ is basically a friendlier API on top of Python’s __instancecheck__ and __subclasscheck__ hooks.) Adapting built-in constructs to work on custom types is very much part of Python’s philosophy.

Python’s source code is exemplary. Here is how collections.Container is defined in the standard library (at time of writing):

class Container(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if any("__contains__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

This definition of __subclasshook__ says that any class with a __contains__ attribute is considered to be a subclass of Container, even if it doesn’t subclass it directly. So I can write this:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

In other words, if you implement the right interface, you’re a subclass! ABCs provide a formal way to define interfaces in Python, while staying true to the spirit of duck-typing. Besides, this works in a way that honours the Open-Closed Principle.

Python’s object model looks superficially similar to that of a more “traditional” OO system (by which I mean Java*) – we got yer classes, yer objects, yer methods – but when you scratch the surface you’ll find something far richer and more flexible. Likewise, Python’s notion of abstract base classes may be recognisable to a Java developer, but in practice they are intended for a very different purpose.

I sometimes find myself writing polymorphic functions that can act on a single item or a collection of items, and I find isinstance(x, collections.Iterable) to be much more readable than hasattr(x, '__iter__') or an equivalent try...except block. (If you didn’t know Python, which of those three would make the intention of the code clearest?)

That said, I find that I rarely need to write my own ABC and I typically discover the need for one through refactoring. If I see a polymorphic function making a lot of attribute checks, or lots of functions making the same attribute checks, that smell suggests the existence of an ABC waiting to be extracted.

*without getting into the debate over whether Java is a “traditional” OO system…


Addendum: Even though an abstract base class can override the behaviour of isinstance and issubclass, it still doesn’t enter the MRO of the virtual subclass. This is a potential pitfall for clients: not every object for which isinstance(x, MyABC) == True has the methods defined on MyABC.

class MyABC(metaclass=abc.ABCMeta):
    def abc_method(self):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return True

class C(object):
    pass

# typical client code
c = C()
if isinstance(c, MyABC):  # will be true
    c.abc_method()  # raises AttributeError

Unfortunately this one of those “just don’t do that” traps (of which Python has relatively few!): avoid defining ABCs with both a __subclasshook__ and non-abstract methods. Moreover, you should make your definition of __subclasshook__ consistent with the set of abstract methods your ABC defines.


回答 2

ABC的一个方便功能是,如果您未实现所有必要的方法(和属性),则在实例化时会出错,而不是AttributeError在实际尝试使用缺少的方法时可能会晚得多。

from abc import ABCMeta, abstractmethod

# python2
class Base(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

# python3
class Base(object, metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

class Concrete(Base):
    def foo(self):
        pass

    # We forget to declare `bar`


c = Concrete()
# TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

来自的例子 https://dbader.org/blog/abstract-base-classes-in-python的

编辑:包括python3语法,谢谢@PandasRocks

A handy feature of ABCs is that if you don’t implement all necessary methods (and properties) you get an error upon instantiation, rather than an AttributeError, potentially much later, when you actually try to use the missing method.

from abc import ABCMeta, abstractmethod

# python2
class Base(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

# python3
class Base(object, metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

class Concrete(Base):
    def foo(self):
        pass

    # We forget to declare `bar`


c = Concrete()
# TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

Example from https://dbader.org/blog/abstract-base-classes-in-python

Edit: to include python3 syntax, thanks @PandasRocks


回答 3

这将使确定对象是否支持给定协议而不必检查协议中所有方法的存在,或者无需由于“不支持”而在“敌人”领域内引发异常就容易得多。

It will make determining whether an object supports a given protocol without having to check for presence of all the methods in the protocol or without triggering an exception deep in “enemy” territory due to non-support much easier.


回答 4

抽象方法确保您在父类中调用的任何方法都必须出现在子类中。以下是noraml调用和使用摘要的方式。用python3编写的程序

正常的通话方式

class Parent:
def methodone(self):
    raise NotImplemented()

def methodtwo(self):
    raise NotImplementedError()

class Son(Parent):
   def methodone(self):
       return 'methodone() is called'

c = Son()
c.methodone()

“称为methodone()”

c.methodtwo()

NotImplementedError

使用抽象方法

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'

c = Son()

TypeError:无法使用抽象方法methodtwo实例化抽象类Son。

由于在子类中未调用methodtwo,因此出现错误。正确的实现如下

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'
    def methodtwo(self):
        return 'methodtwo() is called'

c = Son()
c.methodone()

“称为methodone()”

Abstract method make sure that what ever method you are calling in the parent class has to be appear in child class. Below are noraml way of calling and using abstract. The program written in python3

Normal way of calling

class Parent:
def methodone(self):
    raise NotImplemented()

def methodtwo(self):
    raise NotImplementedError()

class Son(Parent):
   def methodone(self):
       return 'methodone() is called'

c = Son()
c.methodone()

‘methodone() is called’

c.methodtwo()

NotImplementedError

With Abstract method

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'

c = Son()

TypeError: Can’t instantiate abstract class Son with abstract methods methodtwo.

Since methodtwo is not called in child class we got error. The proper implementation is below

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'
    def methodtwo(self):
        return 'methodtwo() is called'

c = Son()
c.methodone()

‘methodone() is called’


是否可以在Python中创建抽象类?

问题:是否可以在Python中创建抽象类?

如何在Python中使类或方法抽象?

我尝试__new__()像这样重新定义:

class F:
    def __new__(cls):
        raise Exception("Unable to create an instance of abstract class %s" %cls)

但是现在,如果我创建一个像这样G继承的F类:

class G(F):
    pass

那么我也无法实例化G,因为它调用了其超类的__new__方法。

有没有更好的方法来定义抽象类?

How can I make a class or method abstract in Python?

I tried redefining __new__() like so:

class F:
    def __new__(cls):
        raise Exception("Unable to create an instance of abstract class %s" %cls)

but now if I create a class G that inherits from F like so:

class G(F):
    pass

then I can’t instantiate G either, since it calls its super class’s __new__ method.

Is there a better way to define an abstract class?


回答 0

使用该abc模块创建抽象类。使用abstractmethod装饰器来声明方法抽象,并根据您的Python版本使用以下三种方式之一声明类抽象。

在Python 3.4及更高版本中,您可以从继承ABC。在Python的早期版本中,您需要将类的元类指定为ABCMeta。指定元类在Python 3和Python 2中具有不同的语法。三种可能性如下所示:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

无论使用哪种方式,都将无法实例化具有抽象方法的抽象类,但将能够实例化提供这些方法的具体定义的子类:

>>> Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
...     pass
... 
>>> StillAbstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
...     def foo(self):
...         print('Hello, World')
... 
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>

Use the abc module to create abstract classes. Use the abstractmethod decorator to declare a method abstract, and declare a class abstract using one of three ways, depending upon your Python version.

In Python 3.4 and above, you can inherit from ABC. In earlier versions of Python, you need to specify your class’s metaclass as ABCMeta. Specifying the metaclass has different syntax in Python 3 and Python 2. The three possibilities are shown below:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

Whichever way you use, you won’t be able to instantiate an abstract class that has abstract methods, but will be able to instantiate a subclass that provides concrete definitions of those methods:

>>> Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
...     pass
... 
>>> StillAbstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
...     def foo(self):
...         print('Hello, World')
... 
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>

回答 1

老式的方法(PEP 3119之前的方法)只是raise NotImplementedError在调用抽象方法的抽象类中进行。

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

它没有与使用abc模块相同的好属性。您仍然可以实例化抽象基类本身,直到在运行时调用抽象方法,您才会发现错误。

但是,如果您要处理的是几套简单的类,也许只有一些抽象方法,则此方法比尝试阅读abc文档要容易一些。

The old-school (pre-PEP 3119) way to do this is just to raise NotImplementedError in the abstract class when an abstract method is called.

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

This doesn’t have the same nice properties as using the abc module does. You can still instantiate the abstract base class itself, and you won’t find your mistake until you call the abstract method at runtime.

But if you’re dealing with a small set of simple classes, maybe with just a few abstract methods, this approach is a little easier than trying to wade through the abc documentation.


回答 2

这是一种非常简单的方法,而无需处理ABC模块。

__init__要成为抽象类的类的方法中,可以检查self的“类型”。如果self的类型是基类,则调用方将尝试实例化基类,因此引发异常。这是一个简单的例子:

class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

运行时,它将生成:

In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__  method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
    raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly

这表明您可以实例化从基类继承的子类,但不能直接实例化基类。

Here’s a very easy way without having to deal with the ABC module.

In the __init__ method of the class that you want to be an abstract class, you can check the “type” of self. If the type of self is the base class, then the caller is trying to instantiate the base class, so raise an exception. Here’s a simple example:

class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

When run, it produces:

In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__  method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
    raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly

This shows that you can instantiate a subclass that inherits from a base class, but you cannot instantiate the base class directly.


回答 3

先前的大多数答案都是正确的,但这是Python 3.7的答案和示例是的,您可以创建一个抽象类和方法。提醒一下,有时一个类应该定义一个逻辑上属于一个类的方法,但是该类无法指定如何实现该方法。例如,在下面的“父母和婴儿”类中,他们都吃东西,但实施方式会有所不同,因为婴儿和父母吃的是不同种类的食物,并且进食的次数不同。因此,eat方法的子类将覆盖AbstractClass.eat。

from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return "eat solid food "+ str(self.value) + " times each day"

class Babies(AbstractClass):
    def eat(self):
        return "Milk only "+ str(self.value) + " times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

输出:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day

Most Previous answers were correct but here is the answer and example for Python 3.7. Yes, you can create an abstract class and method. Just as a reminder sometimes a class should define a method which logically belongs to a class, but that class cannot specify how to implement the method. For example, in the below Parents and Babies classes they both eat but the implementation will be different for each because babies and parents eat a different kind of food and the number of times they eat is different. So, eat method subclasses overrides AbstractClass.eat.

from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return "eat solid food "+ str(self.value) + " times each day"

class Babies(AbstractClass):
    def eat(self):
        return "Milk only "+ str(self.value) + " times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

OUTPUT:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day

回答 4

这将在python 3中工作

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

This one will be working in python 3

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

回答 5

如其他答案所述,是的,您可以使用abc模块在Python中使用抽象类。下面我举个实际的例子使用抽象@classmethod@property@abstractmethod(使用Python 3.6+)。对我而言,通常更容易从示例开始,我可以轻松地复制和粘贴;我希望这个答案对其他人也有用。

首先创建一个名为的基类Base

from abc import ABC, abstractmethod

class Base(ABC):

    @classmethod
    @abstractmethod
    def from_dict(cls, d):
        pass

    @property
    @abstractmethod
    def prop1(self):
        pass

    @property
    @abstractmethod
    def prop2(self):
        pass

    @prop2.setter
    @abstractmethod
    def prop2(self, val):
        pass

    @abstractmethod
    def do_stuff(self):
        pass

我们的Base类将始终具有from_dict classmethod,a property prop1(只读)和a property prop2(也可以设置)以及称为的函数do_stuff。现在构建的任何类都Base将必须为方法/属性实现所有这些。请注意,要使方法抽象,则需要两个装饰器- classmethod和abstract property

现在我们可以创建一个A这样的类:

class A(Base):
    def __init__(self, name, val1, val2):
        self.name = name
        self.__val1 = val1
        self._val2 = val2

    @classmethod
    def from_dict(cls, d):
        name = d['name']
        val1 = d['val1']
        val2 = d['val2']

        return cls(name, val1, val2)

    @property
    def prop1(self):
        return self.__val1

    @property
    def prop2(self):
        return self._val2

    @prop2.setter
    def prop2(self, value):
        self._val2 = value

    def do_stuff(self):
        print('juhu!')

    def i_am_not_abstract(self):
        print('I can be customized')

所有必需的方法/属性均已实现,我们当然可以添加不属于Base(here :)的其他功能i_am_not_abstract

现在我们可以做:

a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})

a1.prop1
# prints 10

a1.prop2
# prints 'stuff'

无法根据需要设置prop1

a.prop1 = 100

将返回

AttributeError:无法设置属性

我们的from_dict方法也可以正常工作:

a2.prop1
# prints 20

如果我们现在这样定义第二个类B

class B(Base):
    def __init__(self, name):
        self.name = name

    @property
    def prop1(self):
        return self.name

并尝试实例化这样的对象:

b = B('iwillfail')

我们会得到一个错误

TypeError:无法使用抽象方法do_stuff,from_dict,prop2实例化抽象类B

列出Base我们未在其中实现的所有定义的事物B

As explained in the other answers, yes you can use abstract classes in Python using the abc module. Below I give an actual example using abstract @classmethod, @property and @abstractmethod (using Python 3.6+). For me it is usually easier to start off with examples I can easily copy&paste; I hope this answer is also useful for others.

Let’s first create a base class called Base:

from abc import ABC, abstractmethod

class Base(ABC):

    @classmethod
    @abstractmethod
    def from_dict(cls, d):
        pass

    @property
    @abstractmethod
    def prop1(self):
        pass

    @property
    @abstractmethod
    def prop2(self):
        pass

    @prop2.setter
    @abstractmethod
    def prop2(self, val):
        pass

    @abstractmethod
    def do_stuff(self):
        pass

Our Base class will always have a from_dict classmethod, a property prop1 (which is read-only) and a property prop2 (which can also be set) as well as a function called do_stuff. Whatever class is now built based on Base will have to implement all of these for methods/properties. Please note that for a method to be abstract, two decorators are required – classmethod and abstract property.

Now we could create a class A like this:

class A(Base):
    def __init__(self, name, val1, val2):
        self.name = name
        self.__val1 = val1
        self._val2 = val2

    @classmethod
    def from_dict(cls, d):
        name = d['name']
        val1 = d['val1']
        val2 = d['val2']

        return cls(name, val1, val2)

    @property
    def prop1(self):
        return self.__val1

    @property
    def prop2(self):
        return self._val2

    @prop2.setter
    def prop2(self, value):
        self._val2 = value

    def do_stuff(self):
        print('juhu!')

    def i_am_not_abstract(self):
        print('I can be customized')

All required methods/properties are implemented and we can – of course – also add additional functions that are not part of Base (here: i_am_not_abstract).

Now we can do:

a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})

a1.prop1
# prints 10

a1.prop2
# prints 'stuff'

As desired, we cannot set prop1:

a.prop1 = 100

will return

AttributeError: can’t set attribute

Also our from_dict method works fine:

a2.prop1
# prints 20

If we now defined a second class B like this:

class B(Base):
    def __init__(self, name):
        self.name = name

    @property
    def prop1(self):
        return self.name

and tried to instantiate an object like this:

b = B('iwillfail')

we will get an error

TypeError: Can’t instantiate abstract class B with abstract methods do_stuff, from_dict, prop2

listing all the things defined in Base which we did not implement in B.


回答 6

这也有效并且很简单:

class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ == "A_abstract": 
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

also this works and is simple:

class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ == "A_abstract": 
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

回答 7

您还可以利用__new__方法来发挥自己的优势。你只是忘记了什么。__new__方法始终返回新对象,因此您必须返回其超类的new方法。进行如下操作。

class F:
    def __new__(cls):
        if cls is F:
            raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
        return super().__new__(cls)

使用新方法时,必须返回对象,而不是None关键字。那就是你所错过的。

You can also harness the __new__ method to your advantage. You just forgot something. The __new__ method always returns the new object so you must return its superclass’ new method. Do as follows.

class F:
    def __new__(cls):
        if cls is F:
            raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
        return super().__new__(cls)

When using the new method, you have to return the object, not the None keyword. That’s all you missed.


回答 8

我发现了可接受的答案,所有其他答案都很奇怪,因为它们传递self给了抽象类。没有实例化抽象类,因此不能具有self

所以尝试一下,它可以工作。

from abc import ABCMeta, abstractmethod


class Abstract(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def foo():
        """An abstract method. No need to write pass"""


class Derived(Abstract):
    def foo(self):
        print('Hooray!')


FOO = Derived()
FOO.foo()

I find the accepted answer, and all the others strange, since they pass self to an abstract class. An abstract class is not instantiated so can’t have a self.

So try this, it works.

from abc import ABCMeta, abstractmethod


class Abstract(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def foo():
        """An abstract method. No need to write pass"""


class Derived(Abstract):
    def foo(self):
        print('Hooray!')


FOO = Derived()
FOO.foo()

回答 9

 from abc import ABCMeta, abstractmethod

 #Abstract class and abstract method declaration
 class Jungle(metaclass=ABCMeta):
     #constructor with default values
     def __init__(self, name="Unknown"):
     self.visitorName = name

     def welcomeMessage(self):
         print("Hello %s , Welcome to the Jungle" % self.visitorName)

     # abstract method is compulsory to defined in child-class
     @abstractmethod
     def scarySound(self):
         pass
 from abc import ABCMeta, abstractmethod

 #Abstract class and abstract method declaration
 class Jungle(metaclass=ABCMeta):
     #constructor with default values
     def __init__(self, name="Unknown"):
     self.visitorName = name

     def welcomeMessage(self):
         print("Hello %s , Welcome to the Jungle" % self.visitorName)

     # abstract method is compulsory to defined in child-class
     @abstractmethod
     def scarySound(self):
         pass

回答 10

在您的代码段中,您还可以通过为__new__子类中的方法提供一个实现来解决此问题,类似地:

def G(F):
    def __new__(cls):
        # do something here

但这是一个hack,除非您知道自己在做什么,否则我建议您不要这样做。对于几乎所有情况,我都建议您使用该abc模块,而我之前的其他人都建议使用该模块。

同样,当您创建一个新的(基)类时,使其成为子类object,如下所示:class MyBaseClass(object):。我不知道它是否还有那么大的意义,但它有助于保持代码的样式一致性

In your code snippet, you could also resolve this by providing an implementation for the __new__ method in the subclass, likewise:

def G(F):
    def __new__(cls):
        # do something here

But this is a hack and I advise you against it, unless you know what you are doing. For nearly all cases I advise you to use the abc module, that others before me have suggested.

Also when you create a new (base) class, make it subclass object, like this: class MyBaseClass(object):. I don’t know if it is that much significant anymore, but it helps retain style consistency on your code


回答 11

只是@TimGilbert的老式答案的快速补充…您可以使抽象基类的init()方法抛出异常,这将阻止实例化它,不是吗?

>>> class Abstract(object):
...     def __init__(self):
...         raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class! 

Just a quick addition to @TimGilbert’s old-school answer…you can make your abstract base class’s init() method throw an exception and that would prevent it from being instantiated, no?

>>> class Abstract(object):
...     def __init__(self):
...         raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class! 

Python中抽象类和接口之间的区别

问题:Python中抽象类和接口之间的区别

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

What is the difference between abstract class and interface in Python?


回答 0

有时您会看到以下内容:

class Abstract1( object ):
    """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没有(也不需要)正式的Interface协定,因此不存在抽象和接口之间的Java风格区别。如果有人努力定义一个正式的接口,它也将是一个抽象类。唯一的区别在于文档字符串中所述的意图。

当您进行鸭类打字时,抽象和接口之间的区别是令人不解的事情。

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

由于Python具有多重继承,因此您可能还会看到类似这样的内容

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

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

What you’ll see sometimes is the following:

class Abstract1( object ):
    """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" )

Because Python doesn’t have (and doesn’t need) a formal Interface contract, the Java-style distinction between abstraction and interface doesn’t exist. If someone goes through the effort to define a formal interface, it will also be an abstract class. The only differences would be in the stated intent in the docstring.

And the difference between abstract and interface is a hairsplitting thing when you have duck typing.

Java uses interfaces because it doesn’t have multiple inheritance.

Because Python has multiple inheritance, you may also see something like this

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

This uses a kind of abstract superclass with mixins to create concrete subclasses that are disjoint.


回答 1

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__。让我们使用文档中的实现示例:

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.ABCMetaabc.abstractmethod在相关方法上使用装饰器来创建自己的抽象基类。元类将被装饰的函数添加到__abstractmethods__属性中,从而防止实例化直到定义它们。

import abc

例如,“有效的”被定义为可以用词表达的东西。假设我们想在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')

现在,如果我们尝试在不实现接口的情况下创建有效对象:

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中的自定义对象定义了接口。

What is the difference between abstract class and interface in Python?

An interface, for an object, is a set of methods and attributes on that object.

In Python, we can use an abstract base class to define and enforce an interface.

Using an Abstract Base Class

For example, say we want to use one of the abstract base classes from the collections module:

import collections
class MySet(collections.Set):
    pass

If we try to use it, we get an TypeError because the class we created does not support the expected behavior of sets:

>>> 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__

So we are required to implement at least __contains__, __iter__, and __len__. Let’s use this implementation example from the 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

Implementation: Creating an Abstract Base Class

We can create our own Abstract Base Class by setting the metaclass to abc.ABCMeta and using the abc.abstractmethod decorator on relevant methods. The metaclass will be add the decorated functions to the __abstractmethods__ attribute, preventing instantiation until those are defined.

import abc

For example, “effable” is defined as something that can be expressed in words. Say we wanted to define an abstract base class that is effable, in Python 2:

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

Or in Python 3, with the slight change in metaclass declaration:

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

Now if we try to create an effable object without implementing the interface:

class MyEffable(Effable): 
    pass

and attempt to instantiate it:

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

We are told that we haven’t finished the job.

Now if we comply by providing the expected interface:

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

we are then able to use the concrete version of the class derived from the abstract one:

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

There are other things we could do with this, like register virtual subclasses that already implement these interfaces, but I think that is beyond the scope of this question. The other methods demonstrated here would have to adapt this method using the abc module to do so, however.

Conclusion

We have demonstrated that the creation of an Abstract Base Class defines interfaces for custom objects in Python.


回答 2

Python> = 2.6具有抽象基类

当诸如hasattr()之类的其他技术笨拙时,抽象基类(缩写为ABC)通过提供一种定义接口的方式来补充鸭式输入。Python随附了许多内置的ABC,用于数据结构(在collections模块中),数字(在numbers模块中)和流(在io模块中)。您可以使用abc模块创建自己的ABC。

还有一个Zope接口模块,该模块由zope外部的项目使用,例如扭曲。我不是很熟悉,但有一个wiki页面在这里可能会有帮助。

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

Python >= 2.6 has Abstract Base Classes.

Abstract Base Classes (abbreviated ABCs) complement duck-typing by providing a way to define interfaces when other techniques like hasattr() would be clumsy. Python comes with many builtin ABCs for data structures (in the collections module), numbers (in the numbers module), and streams (in the io module). You can create your own ABC with the abc module.

There is also the Zope Interface module, which is used by projects outside of zope, like twisted. I’m not really familiar with it, but there’s a wiki page here that might help.

In general, you don’t need the concept of abstract classes, or interfaces in python (edited – see S.Lott’s answer for details).


回答 3

Python实际上没有任何一个概念。

它使用鸭子类型,从而消除了对接口的需求(至少对于计算机:-)。

Python <= 2.5:基类显然存在,但是没有明确的方法将方法标记为“纯虚拟”,因此该类并不是真正的抽象。

Python> = 2.6:确实存在抽象基类(http://docs.python.org/library/abc.html)。并允许您指定必须在子类中实现的方法。我不太喜欢语法,但是功能在那里。在大多数情况下,最好从“使用”客户端使用鸭子类型。

Python doesn’t really have either concept.

It uses duck typing, which removed the need for interfaces (at least for the computer :-))

Python <= 2.5: Base classes obviously exist, but there is no explicit way to mark a method as ‘pure virtual’, so the class isn’t really abstract.

Python >= 2.6: Abstract base classes do exist (http://docs.python.org/library/abc.html). And allow you to specify methods that must be implemented in subclasses. I don’t much like the syntax, but the feature is there. Most of the time it’s probably better to use duck typing from the ‘using’ client side.


回答 4

用更基本的方式解释:接口有点像一个空的松饼锅。这是一个类文件,带有一组没有代码的方法定义。

抽象类是一回事,但并非所有功能都必须为空。有些可以有代码。并非严格意义上是空的。

为什么要区分:Python并没有太大的实际区别,但是在大型项目的计划级别上,谈论接口可能更常见,因为没有代码。尤其是在您与习惯该术语的Java程序员一起工作时。

In a more basic way to explain: An interface is sort of like an empty muffin pan. It’s a class file with a set of method definitions that have no code.

An abstract class is the same thing, but not all functions need to be empty. Some can have code. It’s not strictly empty.

Why differentiate: There’s not much practical difference in Python, but on the planning level for a large project, it could be more common to talk about interfaces, since there’s no code. Especially if you’re working with Java programmers who are accustomed to the term.


回答 5

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

支持多重继承模型的语言倾向于仅使用类或抽象基类,而不使用接口。由于Python支持多重继承,因此它不使用接口,而您想使用基类或抽象基类。

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

In general, interfaces are used only in languages that use the single-inheritance class model. In these single-inheritance languages, interfaces are typically used if any class could use a particular method or set of methods. Also in these single-inheritance languages, abstract classes are used to either have defined class variables in addition to none or more methods, or to exploit the single-inheritance model to limit the range of classes that could use a set of methods.

Languages that support the multiple-inheritance model tend to use only classes or abstract base classes and not interfaces. Since Python supports multiple inheritance, it does not use interfaces and you would want to use base classes or abstract base classes.

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


回答 6

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

Abstract classes are classes that contain one or more abstract methods. Along with abstract methods, Abstract classes can have static, class and instance methods. But in case of interface, it will only have abstract methods not other. Hence it is not compulsory to inherit abstract class but it is compulsory to inherit interface.


回答 7

为了完整起见,我们应该提到PEP3119 ,其中引入了ABC并与接口进行了比较,还有原始的塔林评论。

抽象类不是完美的接口:

  • 属于继承层次
  • 易变

但是,如果您考虑以自己的方式编写它:

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

For completeness, we should mention PEP3119 where ABC was introduced and compared with interfaces, and original Talin’s comment.

The abstract class is not perfect interface:

  • belongs to the inheritance hierarchy
  • is mutable

But if you consider writing it your own way:

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
...

you’ll quite fast realize that you’re inventing the wheel to eventually achieve abc.ABCMeta

abc.ABCMeta was proposed as a useful addition of the missing interface functionality, and that’s fair enough in a language like python.

Certainly, it was able to be enhanced better whilst writing version 3, and adding new syntax and immutable interface concept …

Conclusion:

The abc.ABCMeta IS "pythonic" interface in python