273 votes

Comment créer des propriétés abstraites dans les classes abstraites de python ?

Dans le code suivant, je crée une classe abstraite de base Base . Je veux que toutes les classes qui héritent de Base pour fournir le name J'ai donc fait de cette propriété un @abstractmethod .

J'ai ensuite créé une sous-classe de Base appelé Base_1 qui est censé fournir certaines fonctionnalités, tout en restant abstrait. Il n'y a pas de name propriété en Base_1 mais python instaure néanmoins un objet de cette classe sans erreur. Comment créer des propriétés abstraites ?

from abc import ABCMeta, abstractmethod

class Base(object):
# class Base(metaclass = ABCMeta): <- Python 3
    __metaclass__ = ABCMeta
    def __init__(self, str_dir_config):
        self.str_dir_config = str_dir_config

    @abstractmethod
    def _do_stuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        """This property will be supplied by the inheriting classes
        individually.
        """
        pass

class Base1(Base):
    __metaclass__ = ABCMeta
    """This class does not provide the name property and should
    raise an error.
    """
    def __init__(self, str_dir_config):
        super(Base1, self).__init__(str_dir_config)
        # super().__init__(str_dir_config) <- Python 3

    def _do_stuff(self, signals):
        print "Base_1 does stuff"
        # print("Base_1 does stuff") <- Python 3

class C(Base1):
    @property
    def name(self):
        return "class C"

if __name__ == "__main__":
    b1 = Base1("abc")

6voto

IPlayNaked Points 56

L'utilisation de la @property dans la classe abstraite (comme le recommande le document réponse de James ) fonctionne si vous souhaitez que les attributs requis au niveau de l'instance utilisent également le décorateur de propriétés.

Si vous ne voulez pas utiliser le décorateur de propriétés, vous pouvez utiliser super() . J'ai fini par utiliser quelque chose comme __post_init__() à partir des classes de données et il obtient la fonctionnalité souhaitée pour les attributs au niveau de l'instance :

import abc
from typing import List

class Abstract(abc.ABC):
    """An ABC with required attributes.

    Attributes:
        attr0
        attr1 
    """

    @abc.abstractmethod
    def __init__(self):
        """Forces you to implement __init__ in 'Concrete'. 
        Make sure to call __post_init__() from inside 'Concrete'."""

    def __post_init__(self):
        self._has_required_attributes()
        # You can also type check here if you want.

    def _has_required_attributes(self):
        req_attrs: List[str] = ['attr0', 'attr1']
        for attr in req_attrs:
            if not hasattr(self, attr):
                raise AttributeError(f"Missing attribute: '{attr}'")

class Concrete(Abstract):

    def __init__(self, attr0, attr1):
        self.attr0 = attr0
        self.attr1 = attr1
        self.attr2 = "some value" # not required
        super().__post_init__() # Enforces the attribute requirement.

1voto

Olzhas Arystanov Points 316

Une autre solution possible consiste à utiliser métaclasses .

Un exemple minimal peut se présenter comme suit :

class BaseMetaClass(type):
    def __new__(mcls, class_name, bases, attrs):
        required_attrs = ('foo', 'bar')
        for attr in required_attrs:
            if not attr in attrs:
                raise RunTimeError(f"You need to set {attr} in {class_name}")
        return super().__new__(mcls, class_name, bases, attrs)

class Base(metaclass=BaseMeta):
    foo: str
    bar: int

L'un des avantages de cette approche est que la vérification a lieu au moment de la définition (et non de l'instanciation).

En outre, il est un peu plus facile de définir des attributs de classe dans les classes enfants que de déclarer des propriétés (à condition qu'il s'agisse de valeurs simples connues à l'avance) et vos classes finales auront l'air plus concises

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X