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

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

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

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

class G(F):
    pass

然后,我也不能实例化G,因为它调用它的超类的__new__方法。

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


当前回答

晚些时候回答这里,但要回答另一个问题“如何做出抽象方法”哪一点在这里,我提供如下。

# decorators.py
def abstract(f):
    def _decorator(*_):
        raise NotImplementedError(f"Method '{f.__name__}' is abstract")
    return _decorator


# yourclass.py
class Vehicle:
    def add_energy():
       print("Energy added!")

    @abstract
    def get_make(): ...

    @abstract
    def get_model(): ...

类基类Vehicle仍然可以实例化用于单元测试(与ABC不同),并且python会引发异常。哦,是的,为了方便起见,您还可以使用此方法在异常中获得抽象的方法名。

其他回答

这个将在python3中工作

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

晚些时候回答这里,但要回答另一个问题“如何做出抽象方法”哪一点在这里,我提供如下。

# decorators.py
def abstract(f):
    def _decorator(*_):
        raise NotImplementedError(f"Method '{f.__name__}' is abstract")
    return _decorator


# yourclass.py
class Vehicle:
    def add_energy():
       print("Energy added!")

    @abstract
    def get_make(): ...

    @abstract
    def get_model(): ...

类基类Vehicle仍然可以实例化用于单元测试(与ABC不同),并且python会引发异常。哦,是的,为了方便起见,您还可以使用此方法在异常中获得抽象的方法名。

这里有一个不需要处理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

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

在你的代码片段中,你也可以通过在子类中提供__new__方法的实现来解决这个问题,类似地:

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

但这是一个黑客,我建议你不要这样做,除非你知道你在做什么。对于几乎所有的情况,我建议您使用abc模块,在我之前的其他人已经建议过了。

同样,当你创建一个新的(基)类时,让它成为子类对象,就像这样:我不知道它是否还有那么重要,但它有助于保持代码的风格一致性

你也可以利用__new__方法。你只是忘记了一些东西。 __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关键字。这就是你错过的。