Je veux expliquer en détail la classe abstraite (ABCmeta) de Python

0. Introduction

Parce que la classe abstraite de Python était étonnamment spéciale Je veux expliquer un peu soigneusement en fonction de l'explication de la méta classe

1. Utilisation de base d'ABCmeta

Résumé de Python (classe abstraite) est un peu spécial, quelque chose appelé une métaclasse Définissez une classe en spécifiant ABCmeta (la métaclasse sera expliquée plus loin)

from abc import ABC, ABCMeta, abstractmethod

class Person(metaclass = ABCMeta):
    pass

À propos, comme il existe également une classe ABC, cela ne spécifie pas la classe méta En gros, c'est plus facile à comprendre car il n'hérite que

class Person(ABC):
    pass

Ajoutez @abstractmethod à la méthode que vous souhaitez abstraire

@abstractmethod
def greeting(self):
    pass

Implémenter dans une classe héritée

class Yamada(Person):

    def greeting(self):
        print("Bonjour, c'est Yamada.")

Bien sûr, si vous ne l'implémentez pas et l'instanciez, une erreur se produira.

class Yamada(Person):
    pass

yamada = Yamada()
---------------------------------------------------------------------------------------------------------------
TypeError: Can't instantiate abstract class Yamada with abstract methods greeting

Ce qui précède est comment utiliser la classe abstraite de base

1.1 Qu'est-ce qu'une métaclasse en premier lieu?

Un peu ici, parlez de métaclasses, sautez si vous n'êtes pas intéressé

Qu'est-ce qu'une métaclasse?

En programmation orientée objet, une métaclasse est une classe dont l'instance est une classe. Tout comme une classe régulière définit le comportement de cette instance, une métaclasse définit la classe qui est cette instance, et approfondit le comportement d'une instance de cette classe.

[Source]: Metaclass-wikipedia </ cite>

Je ne suis pas sûr, donc si vous donnez un exemple En fait, la fonction cachée (?) De Python vous permet de définir dynamiquement une classe à partir de type.

def test_init(self):
    pass

Test_Class = type('Test_Class', (object, ), dict(__init__ = test_init, ))

Bien sûr, vous pouvez instancier cette classe

Test_Class_instance = Test_Class()
print(type(Test_Class_instance))
-------------------------------------------------------------------------------------------------------------------
<class '__main__.Test_Class'>

La Test_Class générée par type à ce moment peut être considérée comme une instance ou une définition de classe. C'est une métaclasse au sens strict

Ce qui est surprenant, c'est que les classes qui sont généralement définies avec désinvolture en Python Il peut s'agir d'une instance de type type

class define_class:
    pass

print(type(define_class))
-------------------------------------------------------------------------------------------------------------------
<class 'type'>

Après tout, Python définit une classe en interne type ('classname', ...) </ code> est appelé automatiquement. Il se trouve que

Changer la classe qui peut être appelée automatiquement Une fonction super puissante est l'identité de \ _ \ _ metaclass \ _ \ _, ABCmeta est une classe qui hérite du type Code source méta ABC

À propos, la métaclasse est trop puissante et modifie la spécification du langage elle-même. C’est de la magie noire, vous ne devriez donc pas en abuser.

2. Abstraction de la méthode de classe, de la méthode statique et de la propriété

@abstractclassmethod、@abstractstaticmethod、@abstractproperty Les deux sont obsolètes, il est donc nécessaire d'empiler les décorateurs

Classe abstraite

class Person(ABC):

    @staticmethod
    @abstractmethod
    def age_fudging(age):
        pass

    @classmethod
    @abstractmethod
    def weight_fudging(cls, weight):
        pass

    @property
    @abstractmethod
    def age(self):
        pass

    @age.setter
    @abstractmethod
    def age(self, val):
        pass

    @property
    @abstractmethod
    def weight(self):
        pass

    @weight.setter
    @abstractmethod
    def weight(self, val):
        pass

la mise en oeuvre

class Yamada(Person):

    def __init__(self):
        self.__age = 30
        self.__weight = 120
    
    #Lecture de maquereaux de 10 ans
    @staticmethod
    def age_fudging(age):
        return age - 10
    
    #20kg de maquereau en lecture
    @classmethod
    def weight_fudging(cls, weight):
        return weight - 20
    
    @property
    def age(self):
        return Yamada.age_fudging(self.__age)
    
    @age.setter
    def age(self):
        return
    
    @property
    def weight(self):
        return self.weight_fudging(self.__weight)
    
    @weight.setter
    def weight(self):
        return

y = Yamada()
print("Nom:Âge Yamada:{0}poids:{1}".format(y.age, y.weight))
-----------------------------------------------------------------------------------------------------------------
Nom:Âge Yamada:20 poids:100

Cela fait un peu plus longtemps, mais l'utilisation de base est la même Cependant, veuillez noter que si vous ne faites pas @abstractmethod ci-dessous, vous obtiendrez une erreur.

En passant, en Python, vous pouvez le rendre privé en préfixant la variable membre avec "__".

3. Héritage multiple de classes abstraites

L'héritage multiple est disponible en Python, mais qu'en est-il des classes abstraites?

Comme vous le savez, l'héritage multiple pose divers problèmes (problème d'héritage de diamant, conflit de nom). Fondamentalement, il est plus sûr de ne pas utiliser l'héritage multiple, mais si vous le faites, il est nécessaire qu'il s'agisse d'une classe Mixin.

3.1 Qu'est-ce que Mixin?

mixin est un langage de programmation orienté objet C'est une classe qui fournit des fonctions en étant héritée par des sous-classes et n'est pas destinée à fonctionner indépendamment. Selon la langue Il peut y avoir mixin comme un système distinct de ce que le langage appelle les classes et l'héritage (détaillé dans la section #Variations).

[Source]: Mixin --wikipedia </ cite>

Également cité sur Wikipédia, mais en bref Une classe qui ne fonctionne pas correctement sauf si elle est héritée </ b> Les classes abstraites et les interfaces sont également des classes Mixin

Nous allons donc créer une classe d'homme-bête qui hérite des chats et des humains

class Cat(ABC):

    @abstractmethod
    def mew(self):
        pass
    
    #Aussi en personne
    @abstractmethod
    def sleep(self):
        pass

class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    #Aussi dans Cat
    @abstractmethod
    def sleep(self):
        pass

class Therianthrope(Person, Cat):

    def greeting(self):
        print("Bonjour")

    def mew(self):
        print("Miaou")
    
    def sleep(self):
        print("zzz…")

cat_human = Therianthrope()

cat_human.greeting()
cat_human.mew()
cat_human.sleep()
-----------------------------------------------------------------------------------------------------------------
Bonjour
Miaou
zzz…

Vous pouvez voir que cela fonctionne sans difficulté même avec ABC meta Cette fois, le nom de la méthode appelée sleep est en conflit Il n'y a pas de problème car il n'est pas implémenté dans la source d'héritage

Enfin, comme écrit sur le site officiel de Python Vous devez faire attention aux collisions entre les métaclasses

4. Héritage en plusieurs étapes des classes abstraites

Le problème avec l'héritage en plusieurs étapes est de savoir si ABCmeta héritera une fois hérité En d'autres termes, lorsque Python hérite d'une classe abstraite, il devient une classe abstraite. Le point de devenir

En conclusion, ABCmeta hérite, et si vous héritez d'une classe abstraite, elle devient une classe abstraite.

class Animal(metaclass=ABCMeta):
    pass

class Person(Animal):
    pass

class Yamada(Person):
    pass

print(type(Person))
print(type(Yamada))
-------------------------------------------------------------------------------------------------------------------
<class 'abc.ABCMeta'>
<class 'abc.ABCMeta'>

Après tout, où est la méthode abstraite? Cela signifie qu'il doit être mis en œuvre

class Animal(metaclass=ABCMeta):

    @abstractmethod
    def run(self):
        pass


class Person(Animal):
    pass

class Yamada(Person):

    def run(self):
        print("12 km / h")

y = Yamada()
y.run()
-------------------------------------------------------------------------------------------------------------------
12 km / h

5. Prenez une classe abstraite comme argument formel

Cela fait longtemps, mais à la fin c'est une esquisse approximative de la classe abstraite Présentation d'un exemple d'utilisation qui peut également être utilisé en Python

En fait, même en Python, vous pouvez spécifier le type d'argument formel et la valeur de retour. J'ai spécifié "Person", qui est la classe parente et la classe abstraite créées précédemment, comme type d'argument formel. Créons une fonction appelée fall_asleep

from abstract import Person
import time

def fall_asleep(p:Person):
    sleep_time = p.sleep()
    time.sleep(sleep_time)
    print("!")
class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    @abstractmethod
    def sleep(self) -> float:
        pass

Avec les classes abstraites et le typage, ce n'est plus Python. Ce type de technique est utilisé lors de la création d'un logiciel légèrement plus grand en Python. C'est utile pour quelque chose.

Par exemple, la personne qui fait fall_asleep peut voir l'état réel de la classe concrète (Yamada, Ikeda, Yosida). Ne vous inquiétez pas (je ne sais pas), peu importe combien vous changez, tant que la fonction appelée sommeil répond aux exigences Puisqu'il est terminé, on peut dire que fall_asleep dépend de la classe abstraite (Person) plutôt que de la classe concrète.

C'est ce que l'architecture appelle le «principe d'inversion de dépendance (DIP)». Dire

Les éléments suivants s'appliquent au DIP

  • Ne pas faire référence aux classes concrètes (variables)
  • Ne pas hériter de classes concrètes (variables)
  • Ne remplacez pas les fonctions concrètes

Après tout, il est préférable de s'appuyer sur des classes abstraites Vous pouvez obtenir une architecture robuste.

y = Yamada()
fall_asleep(y)
-------------------------------------------------------------------------------------------------------------------
zzz…
!

Recommended Posts