Personnaliser le comportement dynamique de Python

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Divers aspects du comportement de Python peuvent être personnalisés via des méthodes spéciales ou dites "magiques". Cette section présente cette idée. En outre, l'accès dynamique aux attributs et les méthodes liées sont également discutés.

Introduction

Les classes peuvent définir des méthodes spéciales. Ces méthodes ont un sens particulier pour l'interpréteur Python. Elles sont toujours précédées et suivies de __. Par exemple __init__.

class Stock(object):
    def __init__(self):
     ...
    def __repr__(self):
     ...

Il existe des dizaines de méthodes spéciales, mais nous ne considérerons que quelques exemples spécifiques.

Méthodes spéciales pour les conversions de chaînes de caractères

Les objets ont deux représentations sous forme de chaîne de caractères.

>>> from datetime import date
>>> d = date(2012, 12, 21)
>>> print(d)
2012-12-21
>>> d
datetime.date(2012, 12, 21)
>>>

La fonction str() est utilisée pour créer une sortie imprimable agréable:

>>> str(d)
'2012-12-21'
>>>

La fonction repr() est utilisée pour créer une représentation plus détaillée pour les programmeurs.

>>> repr(d)
'datetime.date(2012, 12, 21)'
>>>

Ces fonctions, str() et repr(), utilisent une paire de méthodes spéciales dans la classe pour produire la chaîne de caractères à afficher.

class Date(object):
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    ## Utilisée avec `str()`
    def __str__(self):
        return f'{self.year}-{self.month}-{self.day}'

    ## Utilisée avec `repr()`
    def __repr__(self):
        return f'Date({self.year},{self.month},{self.day})'

Nota: La convention pour __repr__() est de retourner une chaîne de caractères qui, lorsqu'elle est passée à eval(), recréera l'objet sous-jacent. Si cela n'est pas possible, une représentation facilement lisible est utilisée à la place.

Méthodes spéciales pour les mathématiques

Les opérateurs mathématiques impliquent des appels aux méthodes suivantes.

a + b       a.__add__(b)
a - b       a.__sub__(b)
a * b       a.__mul__(b)
a / b       a.__truediv__(b)
a // b      a.__floordiv__(b)
a % b       a.__mod__(b)
a << b      a.__lshift__(b)
a >> b      a.__rshift__(b)
a & b       a.__and__(b)
a | b       a.__or__(b)
a ^ b       a.__xor__(b)
a ** b      a.__pow__(b)
-a          a.__neg__()
~a          a.__invert__()
abs(a)      a.__abs__()

Méthodes spéciales pour l'accès aux éléments

Ce sont les méthodes pour implémenter des conteneurs.

len(x)      x.__len__()
x[a]        x.__getitem__(a)
x[a] = v    x.__setitem__(a,v)
del x[a]    x.__delitem__(a)

Vous pouvez les utiliser dans vos classes.

class Sequence:
    def __len__(self):
      ...
    def __getitem__(self,a):
      ...
    def __setitem__(self,a,v):
      ...
    def __delitem__(self,a):
      ...

Appel de méthode

L'appel d'une méthode est un processus en deux étapes.

  1. Recherche : L'opérateur .
  2. Appel de méthode : L'opérateur ()
>>> s = stock.Stock('GOOG',100,490.10)
>>> c = s.cost  ## Recherche
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()         ## Appel de méthode
49010.0
>>>

Méthodes liées

Une méthode qui n'a pas encore été appelée par l'opérateur d'appel de fonction () est connue sous le nom de méthode liée. Elle opère sur l'instance à partir de laquelle elle est issue.

>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s
<Stock object at 0x590d0>
>>> c = s.cost
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()
49010.0
>>>

Les méthodes liées sont souvent à l'origine d'erreurs non évidentes dues à la négligence. Par exemple :

>>> s = stock.Stock('GOOG', 100, 490.10)
>>> print('Cost : %0.2f' % s.cost)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: float argument required
>>>

Ou un comportement trompeur difficile à déboguer.

f = open(filename, 'w')
...
f.close     ## Oups, rien n'a été fait du tout. `f` est toujours ouvert.

Dans les deux cas, l'erreur est causée par l'oubli d'inclure les parenthèses finales. Par exemple, s.cost() ou f.close().

Accès aux attributs

Il existe une manière alternative d'accéder, de manipuler et de gérer les attributs.

getattr(obj, 'name')          ## Identique à obj.name
setattr(obj, 'name', value)   ## Identique à obj.name = value
delattr(obj, 'name')          ## Identique à del obj.name
hasattr(obj, 'name')          ## Vérifie si l'attribut existe

Exemple :

if hasattr(obj, 'x'):
    x = getattr(obj, 'x'):
else:
    x = None

*Remarque : getattr() a également une valeur par défaut utile pour l'*argument*.

x = getattr(obj, 'x', None)

Exercice 4.9 : Meilleure sortie pour l'affichage d'objets

Modifiez l'objet Stock que vous avez défini dans stock.py de sorte que la méthode __repr__() produise une sortie plus utile. Par exemple :

>>> goog = stock.Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>

Voyez ce qui se passe lorsque vous lisez un portefeuille d'actions et que vous visualisez la liste résultante après avoir effectué ces modifications. Par exemple :

>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> portfolio
... voir ce que la sortie est...
>>>
✨ Vérifier la solution et pratiquer

Exercice 4.10 : Un exemple d'utilisation de getattr()

getattr() est un mécanisme alternatif pour lire les attributs. Il peut être utilisé pour écrire du code extrêmement flexible. Pour commencer, essayez cet exemple :

>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name','shares']
>>> for colname in columns:
        print(colname, '=', getattr(s, colname))

name = GOOG
shares = 100
>>>

Observez attentivement que les données de sortie sont déterminées entièrement par les noms d'attributs listés dans la variable columns.

Dans le fichier tableformat.py, prenez cette idée et développez-la en une fonction généralisée print_table() qui imprime un tableau montrant les attributs spécifiés par l'utilisateur d'une liste d'objets arbitraires. Comme avec la fonction print_report() précédente, print_table() devrait également accepter une instance TableFormatter pour contrôler le format de sortie. Voici comment cela devrait fonctionner :

>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
      name     shares
---------- ----------
        AA        100
       IBM         50
       CAT        150
      MSFT        200
        GE         95
      MSFT         50
       IBM        100

>>> print_table(portfolio, ['name','shares', 'price'], formatter)
      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44
>>>
✨ Vérifier la solution et pratiquer

Sommaire

Félicitations ! Vous avez terminé le laboratoire sur les méthodes spéciales. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.