如何在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__方法。

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


当前回答

这个方法也很简单:

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()

其他回答

我发现公认的答案和其他答案都很奇怪,因为它们把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()

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

# 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会引发异常。哦,是的,为了方便起见,您还可以使用此方法在异常中获得抽象的方法名。

你也可以利用__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关键字。这就是你错过的。

你可以通过扩展ABC(抽象基类)来创建一个抽象类,并且可以在抽象类中使用@abstractmethod创建抽象方法,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

并且,要使用抽象类,它应该由子类扩展,并且子类应该覆盖抽象类的抽象方法,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Cat(Animal): # Extends "Animal" abstract class
    def sound(self): # Overrides "sound()" abstract method
        print("Meow!!")

obj = Cat()
obj.sound()

输出:

Meow!!

并且,抽象方法可以有code而不是pass,并且可以由子类调用,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        print("Wow!!") # Here

class Cat(Animal):
    def sound(self):
        super().sound() # Here
        
obj = Cat()
obj.sound()

输出:

Wow!!

而且,抽象类可以有变量和非抽象方法,可以由子类调用,非抽象方法不需要被子类覆盖,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass
    
    def __init__(self): # Here
        self.name = "John" # Here
    
    x = "Hello" # Here
    
    def test1(self): # Here
        print("Test1")
    
    @classmethod # Here
    def test2(cls):
        print("Test2")
        
    @staticmethod # Here
    def test3():
        print("Test3")

class Cat(Animal):
    def sound(self):
        print(self.name) # Here
        print(super().x) # Here
        super().test1()  # Here
        super().test2()  # Here
        super().test3()  # Here

obj = Cat()
obj.sound()

输出:

John
Hello
Test1
Test2
Test3

并且,您可以在抽象类中定义抽象类和静态方法以及抽象getter、setter和delete,如下所示。*@abstractmethod必须是最内部的装饰器,否则会发生错误,你可以看到我的回答,解释了更多关于抽象的getter, setter和delete:

from abc import ABC, abstractmethod

class Person(ABC):

    @classmethod
    @abstractmethod # The innermost decorator
    def test1(cls):
        pass
    
    @staticmethod
    @abstractmethod # The innermost decorator
    def test2():
        pass

    @property
    @abstractmethod # The innermost decorator
    def name(self):
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name):
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self):
        pass

然后,你需要在子类中重写它们,如下所示:

class Student(Person):
    
    def __init__(self, name):
        self._name = name
    
    @classmethod
    def test1(cls): # Overrides abstract class method
        print("Test1")
    
    @staticmethod
    def test2(): # Overrides abstract static method
        print("Test2")
    
    @property
    def name(self): # Overrides abstract getter
        return self._name
    
    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name
    
    @name.deleter
    def name(self): # Overrides abstract deleter
        del self._name

然后,你可以实例化子类并调用它们,如下所示:

obj = Student("John") # Instantiates "Student" class
obj.test1() # Class method
obj.test2() # Static method
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))

输出:

Test1
Test2
John 
Tom  
False

并且,如果你尝试实例化一个抽象类,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

obj = Animal()

出现以下错误:

不能实例化抽象类Animal与抽象方法sound

并且,如果你没有在子类中重写抽象类的抽象方法,并且你实例化了子类,如下所示:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Cat(Animal):
    pass # Doesn't override "sound()" abstract method

obj = Cat() # Here

出现以下错误:

不能用抽象方法实例化抽象类Cat

并且,如果你在非抽象类中定义了一个抽象方法,它没有扩展ABC,那么抽象方法是一个正常的实例方法,所以即使非抽象类被实例化,即使子类没有覆盖非抽象类的抽象方法,也不会出现错误,如下所示:

from abc import ABC, abstractmethod

class Animal: # Doesn't extend "ABC"
    @abstractmethod # Here
    def sound(self):
        print("Wow!!")

class Cat(Animal):
    pass # Doesn't override "sound()" abstract method

obj1 = Animal() # Here
obj1.sound()

obj2 = Cat() # Here
obj2.sound()

输出:

Wow!!
Wow!!

另外,你可以替换Cat类扩展下面的Animal class:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

# ↓↓↓ Here ↓↓↓

class Cat(Animal):
    def sound(self):
        print("Meow!!")

# ↑↑↑ Here ↑↑↑

print(issubclass(Cat, Animal))

下面是register()代码:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

# ↓↓↓ Here ↓↓↓

class Cat:
    def sound(self):
        print("Meow!!")
        
Animal.register(Cat)

# ↑↑↑ Here ↑↑↑

print(issubclass(Cat, Animal))

然后,上面的两个代码输出下面相同的结果,显示Cat class是Animal class的子类:

True

使用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>