Python >> Tutoriel Python >  >> Python

Méthode magique Python __get__

__get__() de Python La méthode magique définit la valeur de retour dynamique lors de l'accès à une instance spécifique et à un attribut de classe. Il est défini dans la classe de l'attribut et non dans la classe contenant l'attribut (=le propriétaire classer). Plus précisément, lors de l'accès à l'attribut via la classe propriétaire, Python exécute dynamiquement le __get__() de l'attribut. méthode pour obtenir la valeur de l'attribut.

object.__get__(self, instance, owner=None)

Par exemple, si vous créez une classe Person avec un attribut de type Name (et instances person et name ), en appelant person.name conduirait à appeler le Name __get__() méthode pour obtenir le résultat.

💡 Terminologie :Le name attribut de type Name dans notre exemple s'appelle un descripteur .

Quel est le but de __get__() et des attributs de descripteur

Vous pouvez vous demander :Pourquoi s'embêter à remplacer __get__() ?

Le principal avantage de ceci est que vous pouvez calculer dynamiquement la valeur de l'attribut au moment de l'exécution.

Exemple de base

Dans ce qui suit, vous pouvez voir un exemple minimal d'un attribut de nom de descripteur définissant le __get__ méthode magique pour toujours retourner la même chaîne factice 'Alice Python' .

class Name:   
    def __get__(self, instance, owner=None):
        return 'Alice Python'


class Person:
    name = Name()
    

alice = Person()
print(alice.name)
# Alice Python

Cet exemple n'a pas beaucoup de sens pratique. Le principal avantage de définir un descripteur de type Name est que vous pouvez créer dynamiquement de nouvelles valeurs de retour, chaque fois que vous accédez à l'attribut via la classe propriétaire.

Exemples de valeur d'attribut dynamique

Voici un scénario intéressant, où vous demandez à l'utilisateur de saisir le nom lors de la première connexion :

class Name:
    my_name = ''
    def __get__(self, instance, owner=None):
        while not self.my_name:
            self.my_name = input('your name: ')
        return self.my_name


class Person:
    name = Name()
    

me = Person()
me.name

for i in range(10):
    print('Hello', me.name)

Sortie :

your name: Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python
Hello Alice Python

Notez que dans l'exemple précédent, le programme Python ne demande le nom à l'utilisateur que la première fois que vous y accédez. Les autres fois, il réutilise les informations des premiers accès aux attributs.

En d'autres termes, vous pouvez conserver l'état des accès aux attributs à l'aide du __get__ méthode magique !

Par exemple, vous pouvez également ajouter un compteur pour suivre la fréquence d'accès à l'attribut descripteur :

class Name:
    my_name = ''
    counter = 0
    
    def __get__(self, instance, owner=None):
        self.counter += 1
        print(self.counter)
        
        while not self.my_name:
            self.my_name = input('your name: ')
        return self.my_name


class Person:
    name = Name()
    

me = Person()
me.name

for i in range(10):
    print('Hello', me.name)

Sortie :

1
your name: Alice Python
2
Hello Alice Python
3
Hello Alice Python
4
Hello Alice Python
5
Hello Alice Python
6
Hello Alice Python
7
Hello Alice Python
8
Hello Alice Python
9
Hello Alice Python
10
Hello Alice Python
11
Hello Alice Python

Un autre bon exemple est les recherches dynamiques :

Descripteur de recherche dynamique

L'exemple suivant provient de la documentation :

import os

class DirectorySize:

    def __get__(self, obj, objtype=None):
        return len(os.listdir(obj.dirname))

class Directory:

    size = DirectorySize()              # Descriptor instance

    def __init__(self, dirname):
        self.dirname = dirname          # Regular instance attribute

Vous définissez une classe Directory qui a un attribut de type DirectorySize . Comme la taille d'un répertoire donné n'est connue qu'au moment de l'exécution, vous ne pouvez pas utiliser une valeur constante lors de la première définition du répertoire.

En fait, la taille concrète du répertoire doit être recalculée à chaque fois que vous accédez au répertoire, n'est-ce pas ? Parce que la taille peut être différente à chaque fois que vous y accédez en fonction des fichiers qui ont été ajoutés ou supprimés entre-temps.

C'est là que le __get__() méthode du DirectorySize entre en jeu!

Il recalcule dynamiquement la taille du répertoire en utilisant le os module - chaque fois que vous l'appelez au moment de l'exécution.

Vous pouvez voir comment cela se traduit par différentes valeurs d'attribut à différents moments dans l'exemple de la documentation :

>>> s = Directory('songs')
>>> g = Directory('games')
>>> s.size                              # The songs directory has twenty files
20
>>> g.size                              # The games directory has three files
3
>>> os.remove('games/chess')            # Delete a game
>>> g.size                              # File count is automatically updated
2

Maintenant, vous avez une première intuition. Pour en savoir plus sur le fonctionnement des descripteurs, je vous recommande de consulter le tutoriel Python officiel détaillé qui est une excellente ressource sur le sujet ! 🙂