Можно ли сделать абстрактные классы в 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__ метод.

есть ли лучший способ определить абстрактный класс?

10 ответов


использовать abc модуль для создания абстрактных классов. Используйте abstractmethod decorator для объявления абстрактного метода и объявления абстрактного класса одним из трех способов, в зависимости от вашей версии 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>

старая школа (pre-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 документация.


этот будет работать в 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

вот очень простой способ, без необходимости иметь дело с модулем букварь.

на __init__ метод класса, который вы хотите быть абстрактным классом, вы можете проверить "типа" личности. Если тип 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

Это показывает, что вы можете создать подкласс, который наследует от базового class, но вы не можете создать экземпляр базового класса напрямую.

Ирв


большинство предыдущих ответов были правильными, но вот ответ и пример для Python 3.7. Да, вы можете создать абстрактный класс и метод. Как напоминание иногда класс должен определить метод, который логически принадлежит классу, но этот класс не может указать, как реализовать метод. Например, в приведенных ниже классах родители и дети они оба едят, но реализация будет отличаться для каждого, потому что дети и родители едят разные виды пищи и количество раз, когда они едим разные. Таким образом, метод eat в подклассах переопределяет AbstractClass.есть.

абстрактный класс, ешьте metho

import abc
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 only3 times or more each day

Да, вы можете создавать абстрактные классы в python с модулем abc (абстрактные базовые классы).

этот сайт поможет вам в этом:http://docs.python.org/2/library/abc.html


также это работает и проста:

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

Да, вы можете, используя . abc - модуль абстрактных базовых классов, вот пример:

Питон3.6

# importing abstract base classes module
import abc

class GetterSetter(abc.ABC):
    '''
    ABSTRACT BASE CLASSES:

    -  An abstract base class is a kind of 'model' for other classes to be defined.
        - It is not designed to construct instances, but can be subclassed by regular classes

    - Abstract classes can define interface, or methods that must be implemented by its subclasses.

    '''


    # Abstract classes are not designed to be instantiated, only to be subclassed

    #  decorator for abstract class
    @abc.abstractmethod
    def set_val(self, input):
        """set the value in the instance"""
        return

    @abc.abstractmethod
    def get_val(self):
        """Get and return a value from the instance..."""
        return

# Inheriting from the above abstract class
class MyClass(GetterSetter):

    # methods overriding in the GetterSetter
    def set_val(self, input):
        self.val = input

    def get_val(self):
        return self.val


# Instantiate
x = MyClass()
print(x) # prints the instance <__main__.MyClass object at 0x10218ee48>
x = GetterSetter() #throws error, abstract classes can't be instantiated

вместо python2.x

import abc

class GetterSetter(object):
    # meta class is used to define other classes
    __metaclass__ = abc.ABCMeta


    #  decorator for abstract class
    @abc.abstractmethod
    def set_val(self, input):
        """set the value in the instance"""
        return

    @abc.abstractmethod
    def get_val(self):
        """Get and return a value from the instance..."""
        return

# Inheriting from the above abstract class
class MyClass(GetterSetter):

    # methods overriding in the GetterSetter
    def set_val(self, input):
        self.val = input

    def get_val(self):
        return self.val


# Instantiate
x = GetterSetter()
print(x)
x = GetterSetter() #throws error, abstract classes can't be instantiated

в фрагменте кода Вы также можете решить эту проблему, предоставив реализацию для __new__ метод в подклассе, кроме того:

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

но это взлом, и я советую вам не делать этого, если вы не знаете, что делаете. Для почти всех случаев я советую вам использовать abc модуль, который предложили другие до меня.

также, Когда вы создаете новый (базовый) класс, сделайте его подклассом object, например: class MyBaseClass(object):. Я не знаю, так ли это много. значительно больше, но это помогает сохранить согласованность стилей в вашем коде


просто быстрое дополнение к ответу старой школы @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!