Python中的抽象方法[重复]

Posted

技术标签:

【中文标题】Python中的抽象方法[重复]【英文标题】:Abstract methods in Python [duplicate] 【发布时间】:2011-05-21 22:37:07 【问题描述】:

我在使用 Python 继承时遇到问题。虽然这个概念在 Java 中对我来说似乎太容易了,但到目前为止,我在 Python 中一直无法理解,这至少让我感到惊讶。

我有一个原型如下:

class Shape():
   def __init__(self, shape_name):
       self.shape = shape_name

class Rectangle(Shape):
   def __init__(self, name):
       self.shape = name

在上面的代码中,如何创建一个需要为所有子类实现的抽象方法?

【问题讨论】:

顺便说一句,这确实是python中的一个事后想法。抽象接口等是最近才引入的。由于 python 没有被编译,你应该在文档而不是抽象类中明确你的意图。 也是一个非常很好的单元测试应用程序。由于 Python 未编译,因此在对代码进行单元测试之前,我不会开始信任代码。 【参考方案1】:

在引入 abc 之前,您会经常看到这种情况。

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


class Specialized(Base):
    def go(self):
        print "Consider me implemented"

【讨论】:

在我看来,这比公认的答案更 Pythonic,因为它遵循 Python 格言“我们都是成年人”——如果子类不实现这种类型的抽象方法,它将必须承担后果 @EmmettJ.Butler 但是abc毕竟应该实现。我说的对吗? @Spiderman 不,abc 不是用于简单的基类,abc 是当您需要 isinstance 检查时,类似于 isinstance(x, number) 或 isinstance(x, collections.abc.Sequence)。跨度> @EmmettJ.Butler 但它没有提供任何即时反馈。它只有在方法使用后才提供反馈,这使得该机制作为抽象类完全没有用。【参考方案2】:

类似这样的东西,使用 ABC

import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta
    
    @abc.abstractmethod
    def method_to_implement(self, input):
        """Method documentation"""
        return
    

还可以阅读这篇好教程:https://pymotw.com/3/abc/

您还可以查看在 python 中引入 ABC 之前使用的 zope.interface

http://pypi.python.org/pypi/zope.interface https://zopeinterface.readthedocs.io/en/latest/README.html

【讨论】:

添加这个会影响其他东西吗??即我的课程代码的其他部分是否需要更改?? @user506710:您的代码中唯一可能需要更改的部分是您是否已经在使用元类。在这种情况下,只需让您的元类派生自 abc.ABCMeta 而不是 type 就可以了。如果您不知道什么是元类,请不要担心。 :-) 链接的教程比 Python abc doc (docs.python.org/2/library/abc.html) 清晰很多;然而他们说同样的事情 此语法在 python 3 中不起作用,请参阅此问题/答案:***.com/a/18513858/1240268。 在 python 3 中,您应该改用这个答案。 ***.com/a/13646263/5093308【参考方案3】:

请参阅abc module。基本上,您在类上定义__metaclass__ = abc.ABCMeta,然后用@abc.abstractmethod 装饰每个抽象方法。除非所有抽象方法都被覆盖,否则从此类派生的类不能被实例化。

如果您的类已经在使用元类,请从ABCMeta 而不是type 派生它,您可以继续使用自己的元类。

一种廉价的替代方法(以及在引入 abc 模块之前的最佳实践)是让所有抽象方法只引发异常(NotImplementedError 是一个很好的方法),以便从它派生的类必须覆盖该方法是有用的。

但是,abc 解决方案更好,因为它可以防止此类类被实例化(即,它“失败得更快”),还因为您可以提供每个可以达到的方法的默认或基本实现在派生类中使用super() 函数。

【讨论】:

you can provide a default or base implementation of each method that can be reached using the super() function in derived classes 你不能在没有abc 的情况下使用super() 它的意思是你必须使用super() 来访问ABC 中的方法实现,而不是你需要使用abc 才能使用super()super() 适用于任何新式类。 我只是感到困惑,因为它是“abc 更适合动机 1 和动机 2”但动机 2 不是 abc 特定的:)【参考方案4】:

抽象基类是深奥的魔法。我会定期使用它们来实现一些东西,并对自己的聪明感到惊讶,不久之后我发现自己完全被自己的聪明搞糊涂了(不过这很可能只是个人的限制)。

另一种方法(如果你问我应该在 python 标准库中)是制作一个装饰器。

def abstractmethod(method):
    """
    An @abstractmethod member fn decorator.
    (put this in some library somewhere for reuse).

    """
    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method ' 
                                  + repr(method))
    default_abstract_method.__name__ = method.__name__    
    return default_abstract_method


class Shape(object):

    def __init__(self, shape_name):
       self.shape = shape_name

    @abstractmethod
    def foo(self):
        print "bar"
        return

class Rectangle(Shape):
    # note you don't need to do the constructor twice either
    pass  

r = Rectangle("x")
r.foo()

装饰器不是我写的。我突然想到有人会有。你可以在这里找到它:http://code.activestate.com/recipes/577666-abstract-method-decorator/ 好一个 jimmy2times。请注意该页面底部的讨论。装饰器的类型安全。 (如果有人愿意,可以使用检查模块修复)。

【讨论】:

这与abc.abstractmethod有何不同? 没什么大不了的。在引擎盖下,我想它们并没有太大的不同。务实:“使用 abc.abstractmethod 装饰器要求类的元类是 ABCMeta 或派生自它。”。【参考方案5】:

你不能,使用语言原语。如前所述,abc package 在 Python 2.6 及更高版本中提供了此功能,但在 Python 2.5 及更早版本中没有选项。 abc 包不是 Python 的新特性;相反,它通过添加明确的“这个类是否说它这样做?”来增加功能。检查,使用手动实现的一致性检查,如果此类声明错误,则会在初始化期间导致错误。

Python 是一种激进的动态类型语言。它没有指定语言原语以允许您阻止程序因为对象不符合类型要求而编译;这只能在运行时发现。如果您要求子类实现一个方法,请记录该方法,然后盲目地调用该方法,希望它会在那里。

如果它在那里,太棒了,它很有效;这被称为duck typing,并且您的对象已经像鸭子一样嘎嘎作响,足以满足接口。这工作得很好即使self 是您在其上调用此类方法的对象,由于需要特定功能实现的基本方法(通用函数),出于强制覆盖的目的,因为self 是一种约定,实际上并没有什么特别之处。

__init__ 中的例外是,因为当您的初始化程序被调用时,派生类型的初始化程序没有,所以它还没有机会将自己的方法装订到对象上。

如果方法没有实现,你会得到一个 AttributeError (如果它根本不存在)或一个 TypeError (如果有那个名字的东西但它不是一个函数或者它没有那个签名) .这取决于你如何处理它 - 要么称之为程序员错误并让它崩溃程序(它“应该”对python开发人员来说是显而易见的,是什么导致了这种错误 - 一个未满足的鸭子接口),或者捕获并处理当您发现您的对象不支持您希望它做的事情时,这些例外情况。实际上,捕获 AttributeError 和 TypeError 在很多情况下都很重要。

【讨论】:

那么在另一个答案中提到的 abc 模块呢?? 不管怎样,Python 强类型的,它只是动态类型的。 确认。这是我第一次听说abc 模块。不过,我将留下这个答案,因为它适用于 2.6 之前的 Python 版本(CentOS 附带 2.4),并且鸭式打字在这里仍然有效。 是的,这是关于鸭子打字的一个好点。通常,在 Python 中编程,您通常会更乐意调用您期望的方法,而不是强制执行真正的类型约束。不过,abc 是指定 哪种 你想要的鸭子的好选择。 python 肯定是强类型的。他指的是静态与动态类型。【参考方案6】:

你可以使用六和abc高效地为python2和python3构造一个类,如下所示:

import six
import abc

@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
    """
    documentation
    """

    @abc.abstractmethod
    def initialize(self, para=None):
        """
        documentation
        """
        raise NotImplementedError

This is an awesome document of it.

【讨论】:

以上是关于Python中的抽象方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Python 3.5中抽象方法的主体[重复]

python中的抽象方法

java接口上的抽象方法是啥[重复]

Python中的抽象方法

如何从同一个类中的另一个构造函数调用抽象类的构造函数(方法重载)[重复]

不是抽象的,也不会覆盖抽象方法[重复]