Est-il possible de faire des classes abstraites en Python?

comment rendre une classe ou une méthode abstraite en Python?

j'ai essayé de redéfinir la notion de __new__() comme suit:

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

mais maintenant si je crée une classe G qui hérite de F comme ainsi:

class G(F):
    pass

alors je ne peux pas instancier G non plus, puisqu'il appelle sa classe super la méthode __new__ .

Est-il un meilleur moyen de définir une classe abstraite?

186
demandé sur Martin Thoma 2012-11-30 17:29:16

10 réponses

utilisez le module abc pour créer des classes abstraites. Utilisez le abstractmethod décorateur pour déclarer un résumé de méthode, et déclarer un résumé de classe en utilisant l'une des trois façons, en fonction de votre version Python.

en Python 3.4 et au-dessus, vous pouvez hériter de ABC . Dans les versions précédentes de Python, vous devez spécifier le metaclass de votre classe comme ABCMeta . Spécifier le metaclass a une syntaxe différente en Python 3 et Python 2. Les trois possibilités sont indiquées ci-dessous:

# 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

quelle que soit la façon dont vous l'utilisez, vous ne pourrez pas instancier une classe abstraite qui a des méthodes abstraites, mais sera capable d'instancier une sous-classe qui fournit des définitions concrètes de ces méthodes:

>>> 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>
366
répondu alexvassel 2018-03-16 09:48:51

La vieille école (pré- PEP 3119 ) façon de le faire c'est juste pour raise NotImplementedError dans la classe abstraite lorsqu'une méthode abstraite est appelé.

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): [...]

cela n'a pas les mêmes propriétés agréables qu'utiliser le module abc . Vous pouvez encore instancier la classe abstract base elle-même, et vous ne trouverez pas votre erreur tant que vous n'appellerez pas la méthode abstraite à l'exécution.

mais si vous avez affaire à un petit ensemble de des classes simples, peut-être avec seulement quelques méthodes abstraites, cette approche est un peu plus facile que d'essayer de parcourir la documentation abc .

66
répondu Tim Gilbert 2016-06-30 16:30:24

celui-ci fonctionnera en 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
8
répondu Rajkumar SR 2017-11-07 05:59:21

voici un moyen très facile sans avoir à traiter avec le module ABC.

Dans le __init__ méthode de la classe que vous voulez être une classe abstraite, vous pouvez cocher la case "type" de l'auto. Si le type de soi est la classe de base, alors l'appelant essaye d'instancier la classe de base, donc de soulever une exception. Voici un exemple simple:

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

Lorsqu'il est exécuté, il produit:

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

Cela montre que vous pouvez instanciez une sous-classe qui hérite d'une classe de base, mais vous ne pouvez pas instancier directement la classe de base.

Irv

5
répondu Irv Kalb 2018-01-16 05:37:16

la plupart des réponses précédentes étaient correctes, mais voici la réponse et l'exemple pour Python 3.7. Oui,vous pouvez créer la classe abstraite et la méthode. Tout comme un rappel parfois une classe devrait définir une méthode qui appartient logiquement à une classe, mais cette classe ne peut pas spécifier comment implémenter la méthode. Par exemple, dans les classes ci-dessous, les Parents et les bébés mangent tous les deux, mais la mise en œuvre sera différente pour chacun parce que les bébés et les parents mangent différents types de nourriture et le nombre de fois ils mangent est différent. Ainsi, la méthode eat dans les sous-classes l'emporte sur AbstractClass.manger.

AbstractClass, manger 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())

sortie:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only3 times or more each day
2
répondu CPU 100 2018-09-10 05:01:32

Oui, vous pouvez créer des classes abstraites en python avec le module abc (classes de base abstraites).

ce site vous aidera avec lui: http://docs.python.org/2/library/abc.html

1
répondu ORION 2012-11-30 13:32:12

cela aussi fonctionne et est 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()
1
répondu Giovanni Angeli 2018-01-12 14:00:15

Oui, vous pouvez, en utilisant le . abc - Abstract Base Classes module, voici un exemple:

Python3.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
0
répondu rzskhr 2017-11-04 15:36:25

dans votre extrait de code, vous pouvez également résoudre cela en fournissant une implémentation pour la méthode __new__ dans la sous-classe, de même:

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

mais c'est un piratage et je vous le déconseille, à moins que vous ne sachiez ce que vous faites. Pour presque tous les cas, je vous conseille d'utiliser le module abc , que d'autres avant moi ont suggéré.

même si vous créez une nouvelle classe (de base), faites la sous-classe object , comme ceci: class MyBaseClass(object): . Je ne sais pas si c'est si important que ça, mais ça aide à garder la cohérence du style sur votre code

-1
répondu NlightNFotis 2012-11-30 15:05:37

juste un petit ajout à la réponse de l'ancienne école de @TimGilbert...vous pouvez faire votre classe de base abstraite INIT () méthode jeter une exception et qui l'empêcherait d'être instancié, Non?

>>> 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! 
-1
répondu Dave Wade-Stein 2017-08-07 23:10:04