Comment créer un résumé de classe ou de méthode en Python?
J'ai essayé de redéfinir __new__()
comme ceci:
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 ceci:
class G(F):
pass
alors je ne peux pas non plus instancier G
, puisqu'il appelle la méthode __new__
de sa super-classe.
Existe-t-il un meilleur moyen de définir une classe abstraite?
Utilisez le module abc
pour créer des classes abstraites. Utilisez le décorateur abstractmethod
pour déclarer un résumé de méthode et déclarer un résumé de classe de trois façons, selon votre version Python.
Dans Python 3.4 et versions ultérieures, vous pouvez hériter de ABC
. Dans les versions antérieures de Python, vous devez spécifier la métaclasse de votre classe sous la forme ABCMeta
. La spécification de la métaclasse a une syntaxe différente dans Python 3 et Python 2. Les trois possibilités sont présenté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 méthode utilisée, vous ne pourrez pas instancier une classe abstraite dotée de méthodes abstraites, mais vous pourrez instancier une sous-classe fournissant 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>
Pour ce faire, la méthode old-school (pre - PEP 3119 ) consiste simplement à raise NotImplementedError
dans la classe abstraite lorsqu’une méthode abstraite est appelée.
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 de Nice que le module abc
. Vous pouvez toujours instancier la classe de base abstraite elle-même et vous ne trouverez pas votre erreur jusqu'à ce que vous appeliez la méthode abstraite au moment de l'exécution.
Mais si vous traitez avec un petit ensemble de classes simples, peut-être avec quelques méthodes abstraites, cette approche est un peu plus facile que d'essayer de parcourir la documentation abc
.
Voici un moyen très facile sans avoir à traiter avec le module ABC.
Dans la méthode __init__
de la classe pour laquelle vous voulez être une classe abstraite, vous pouvez vérifier le "type" de self. Si le type de self est la classe de base, alors l'appelant tente d'instancier la classe de base, alors déclenchez 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 instancier une sous-classe qui hérite d'une classe de base, mais que vous ne pouvez pas instancier directement la classe de base.
Irv
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 une classe et une méthode abstraites. Tout comme un rappel, une classe doit parfois 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 cours Parents et Bébés ci-dessous, ils mangent tous les deux, mais la mise en œuvre sera différente pour chacun, car les bébés et les parents mangent un type de nourriture différent et le nombre de fois qu'ils le mangent est différent. Donc, les sous-classes de méthode eat remplacent AbstractClass.eat.
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 only 3 times or more each day
Celui-ci fonctionnera dans 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
Comme expliqué dans les autres réponses, vous pouvez utiliser des classes abstraites dans Python à l'aide du module abc
. Ci-dessous, je donne un exemple réel en utilisant abstract @classmethod
, @property
et @abstractmethod
(en utilisant Python 3.6+). Pour moi, il est généralement plus facile de commencer avec des exemples que je peux facilement copier et coller; J'espère que cette réponse est également utile pour les autres.
Commençons par créer une classe de base appelée Base
:
from abc import ABC, abstractmethod
class Base(ABC):
@classmethod
@abstractmethod
def from_dict(cls, d):
pass
@property
@abstractmethod
def prop1(self):
pass
@property
@abstractmethod
def prop2(self):
pass
@prop2.setter
@abstractmethod
def prop2(self, val):
pass
@abstractmethod
def do_stuff(self):
pass
Notre classe Base
aura toujours un from_dict
classmethod
, un property
prop1
(en lecture seule) et un property
prop2
(qui peut également être défini), ainsi qu'une fonction appelée do_stuff
. Quelle que soit la classe construite à partir de Base
, elle devra toutes les implémenter pour les méthodes/propriétés. Veuillez noter que pour le résumé classmethod
et le résumé property
, deux décorateurs sont requis.
Maintenant, nous pourrions créer une classe A
comme ceci:
class A(Base):
def __init__(self, name, val1, val2):
self.name = name
self.__val1 = val1
self._val2 = val2
@classmethod
def from_dict(cls, d):
name = d['name']
val1 = d['val1']
val2 = d['val2']
return cls(name, val1, val2)
@property
def prop1(self):
return self.__val1
@property
def prop2(self):
return self._val2
@prop2.setter
def prop2(self, value):
self._val2 = value
def do_stuff(self):
print('juhu!')
def i_am_not_abstract(self):
print('I can be customized')
Toutes les méthodes/propriétés requises sont implémentées et nous pouvons - bien sûr - également ajouter des fonctions supplémentaires qui ne font pas partie de Base
(ici: i_am_not_abstract
).
Maintenant nous pouvons faire:
a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})
a1.prop1
# prints 10
a1.prop2
# prints 'stuff'
Comme vous le souhaitez, nous ne pouvons pas définir prop1
:
a.prop1 = 100
reviendra
AttributeError: impossible de définir l'attribut
Notre méthode from_dict
fonctionne aussi très bien:
a2.prop1
# prints 20
Si nous définissons maintenant une seconde classe B
comme ceci:
class B(Base):
def __init__(self, name):
self.name = name
@property
def prop1(self):
return self.name
et essayé d'instancier un objet comme celui-ci:
b = B('iwillfail')
nous aurons une erreur
TypeError: Impossible d'instancier la classe abstraite B avec les méthodes abstraites do_stuff, from_dict, prop2
listant toutes les choses définies dans Base
que nous n'avons pas implémentées dans B
.
Oui, vous pouvez créer des classes abstraites dans python avec le module abc (abstract base classes).
Ce site vous aidera avec: http://docs.python.org/2/library/abc.html
Vous pouvez également exploiter la méthode __new__ à votre avantage. Tu as juste oublié quelque chose. La méthode __new__ renvoie toujours le nouvel objet. Vous devez donc renvoyer la nouvelle méthode de sa superclasse. Faites comme suit.
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)
Lorsque vous utilisez la nouvelle méthode, vous devez renvoyer l'objet et non le mot clé None. C'est tout ce que tu as manqué.
cela fonctionne aussi et est simple:
class A_abstract(object):
def __init__(self):
# quite simple, old-school way.
if self.__class__.__== "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()