Méthodes magiques numériques personnalisées

PythonPythonBeginner
Pratiquer maintenant

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

Introduction

Dans ce tutoriel, nous allons aborder les méthodes magiques Python liées aux opérations numériques. Les méthodes magiques sont des méthodes spéciales dans les classes Python qui commencent et finissent par deux tirets bas (__). Elles sont également connues sous le nom de méthodes "dunder" (deux tirets bas).

Ces méthodes magiques vous permettent de définir comment les instances de votre classe se comportent avec certaines opérations, comme l'addition ou la soustraction.

Nous aborderons les sections suivantes :

  1. Opérateurs unaire
  2. Opérateurs binaire
  3. Opérations in-place

Commençons!


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/BasicConceptsGroup -.-> python/numeric_types("Numeric Types") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") subgraph Lab Skills python/numeric_types -.-> lab-7838{{"Méthodes magiques numériques personnalisées"}} python/classes_objects -.-> lab-7838{{"Méthodes magiques numériques personnalisées"}} end

Opérateurs unaire

Les opérations unaire sont des opérations qui impliquent un seul opérande, telles que la négation, la valeur absolue, etc.

Commencons par un objet simple. Dans number.py, créez une classe nommée MyNumber qui ait un attribut value.

class MyNumber:
    def __init__(self, value: float):
        self.value = value

__neg__

La méthode magique __neg__ définit comment doit se comporter l'opération de négation. Lorsque vous utilisez l'opérateur - sur une instance de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __neg__(self) -> 'MyNumber':
        """Renvoie la négation de la valeur de l'instance."""
        return MyNumber(-self.value)

__abs__

La méthode magique __abs__ définit comment doit se comporter l'opération de valeur absolue. Lorsque vous utilisez la fonction abs() sur une instance de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __abs__(self) -> 'MyNumber':
        """Renvoie la valeur absolue de la valeur de l'instance."""
        return MyNumber(abs(self.value))

__round__

La méthode magique __round__ définit comment doit se comporter l'opération d'arrondi. Lorsque vous utilisez la fonction round() sur une instance de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __round__(self, ndigits: int = None) -> 'MyNumber':
        """Arrondit la valeur de l'instance au nombre entier le plus proche ou au nombre spécifié de chiffres."""
        return MyNumber(round(self.value, ndigits))

__floor__

La méthode magique __floor__ définit comment doit se comporter l'opération de sol. Lorsque vous utilisez la fonction math.floor() sur une instance de votre classe, cette méthode est appelée.

## Le module math doit être importé en haut de number.py
import math

    #... (code précédent dans number.py)

    def __floor__(self) -> 'MyNumber':
        """Renvoie le plus grand entier inférieur ou égal à la valeur de l'instance."""
        return MyNumber(math.floor(self.value))

__ceil__

La méthode magique __ceil__ définit comment doit se comporter l'opération de plafond. Lorsque vous utilisez la fonction math.ceil() sur une instance de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __ceil__(self) -> 'MyNumber':
        """Renvoie le plus petit entier supérieur ou égal à la valeur de l'instance."""
        return MyNumber(math.ceil(self.value))

Exemple : Utilisation des opérateurs unaire

Maintenant que nous avons défini les opérateurs unaire pour notre classe MyNumber, voyons comment ils fonctionnent dans unary_example.py :

import math
from number import MyNumber

## Crée un nouvel objet MyNumber
a = MyNumber(5)
## Utilise la méthode __neg__ avec la fonction print
print(f'{a.value=}, {-a.value=}')  ## Sortie : a.value=5, -a.value=-5

## Crée un autre nouvel objet MyNumber
a = MyNumber(-5)
## Utilise la méthode __abs__ avec la fonction print
print(f'{a.value=}, {abs(a).value=}')  ## Sortie : a.value=-5, abs(a).value=5

## Crée le troisième nouvel objet MyNumber
a = MyNumber(5.678)
## Utilise la méthode __round__ avec la fonction print
print(f'{a.value=}, {round(a, 2).value=}')  ## Sortie : a.value=5.678, round(a, 2).value=5.68

## Utilise la méthode __floor__ avec la fonction print
print(f'{a.value=}, {math.floor(a).value=}')  ## Sortie : a.value=5.678, math.floor(a).value=5

## Utilise la méthode __ceil__ avec la fonction print
print(f'{a.value=}, {math.ceil(a).value=}')  ## Sortie : a.value=5.678, math.ceil(a).value=6

Ensuite, tapez la commande suivante dans le terminal pour exécuter le script.

python unary_example.py

Opérateurs binaire

Les opérations binaire sont des opérations qui impliquent deux opérandes, telles que les opérations arithmétiques comme l'addition, la soustraction, la multiplication et la division, ainsi que les opérations de comparaison comme l'égalité, l'inégalité, inférieur à, supérieur à, etc.

__add__

La méthode magique __add__ définit comment doit se comporter l'opération d'addition. Lorsque vous utilisez l'opérateur + sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __add__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie la somme de la valeur de l'instance et de la valeur de l'autre instance."""
        return MyNumber(self.value + other.value)

__sub__

La méthode magique __sub__ définit comment doit se comporter l'opération de soustraction. Lorsque vous utilisez l'opérateur - sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __sub__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie la différence entre la valeur de l'instance et la valeur de l'autre instance."""
        return MyNumber(self.value - other.value)

__mul__

La méthode magique __mul__ définit comment doit se comporter l'opération de multiplication. Lorsque vous utilisez l'opérateur * sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __mul__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie le produit de la valeur de l'instance et de la valeur de l'autre instance."""
        return MyNumber(self.value * other.value)

__truediv__

La méthode magique __truediv__ définit comment doit se comporter l'opération de division réelle. Lorsque vous utilisez l'opérateur / sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __truediv__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie le résultat de la division de la valeur de l'instance par la valeur de l'autre instance."""
        return MyNumber(self.value / other.value)

__floordiv__

La méthode magique __floordiv__ définit comment doit se comporter l'opération de division entière. Lorsque vous utilisez l'opérateur // sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __floordiv__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie le plus grand entier inférieur ou égal au résultat de la division de la valeur de l'instance par la valeur de l'autre instance."""
        return MyNumber(self.value // other.value)

__mod__

La méthode magique __mod__ définit comment doit se comporter l'opération de modulo. Lorsque vous utilisez l'opérateur % sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __mod__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie le reste de la division de la valeur de l'instance par la valeur de l'autre instance."""
        return MyNumber(self.value % other.value)

__pow__

La méthode magique __pow__ définit comment doit se comporter l'opération de puissance. Lorsque vous utilisez l'opérateur ** ou la fonction pow() sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __pow__(self, other: 'MyNumber') -> 'MyNumber':
        """Renvoie la valeur de l'instance élevée à la puissance de la valeur de l'autre instance."""
        return MyNumber(self.value ** other.value)

Exemple : Utilisation des opérateurs binaire

Maintenant que nous avons défini les opérateurs binaire pour notre classe MyNumber, voyons comment ils fonctionnent dans binary_example.py :

from number import MyNumber

## Crée deux nouveaux objets MyNumber
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## Sortie : a.value=5, b.value=3

## Utilise la méthode __add__ avec la fonction print
print(f'{(a+b).value=}')  ## Sortie : (a+b).value=8

## Utilise la méthode __sub__ avec la fonction print
print(f'{(a-b).value=}')  ## Sortie : (a-b).value=2

## Utilise la méthode __mul__ avec la fonction print
print(f'{(a*b).value=}')  ## Sortie : (a*b).value=15

## Utilise la méthode __truediv__ avec la fonction print
print(f'{(a/b).value=}')  ## Sortie : (a/b).value=1.6666666666666667

## Utilise la méthode __floordiv__ avec la fonction print
print(f'{(a//b).value=}')  ## Sortie : (a//b).value=1

## Utilise la méthode __mod__ avec la fonction print
print(f'{(a%b).value=}')  ## Sortie : (a%b).value=2

## Utilise la méthode __pow__ avec la fonction print
print(f'{(a**b).value=}')  ## Sortie : (a**b).value=125

Ensuite, tapez la commande suivante dans le terminal pour exécuter le script.

python binary_example.py

Opérations in-place

Les opérations in-place sont des opérations qui modifient la valeur d'un objet en place, sans créer un nouvel objet. Elles sont désignées par les opérateurs d'affectation augmentée tels que +=, -=, *=, /=, etc.

Si l'opérateur in-place n'est pas défini pour une classe Python, alors l'opérateur binaire sera utilisé à la place lorsqu'une opération in-place est tentée.

Voici un exemple dans inplace_example1.py, changez les opérateurs binaires en opérateurs in-place :

from number import MyNumber

## Crée deux nouveaux objets MyNumber
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## Sortie : a.value=5, b.value=3

a += b
## Utilise la méthode __add__ avec la fonction print
print(f'après a+=b: {a.value=}')  ## Sortie : après a+=b: (a+b).value=8

Pour exécuter l'exemple, tapez la commande suivante dans le terminal :

python inplace_example1.py

Le résultat montre que la méthode __add__ a été utilisée lors de l'opération +=.

Ensuite, nous allons implémenter les opérations in-place dans MyNumber, et leur comportement avec les opérateurs binaires correspondants sera légèrement différent.

__iadd__

La méthode magique __iadd__ définit comment doit se comporter l'opération d'addition in-place. Lorsque vous utilisez l'opérateur += sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __iadd__(self, other: 'MyNumber') -> 'MyNumber':
        """Ajoute la valeur de l'autre instance à la valeur de l'instance en place."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value += other.value
        print(f'après += : {self.value=}')
        return self

__isub__

La méthode magique __isub__ définit comment doit se comporter l'opération de soustraction in-place. Lorsque vous utilisez l'opérateur -= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __isub__(self, other: 'MyNumber') -> 'MyNumber':
        """Soustrait la valeur de l'autre instance de la valeur de l'instance en place."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value -= other.value
        print(f'après -= : {self.value=}')
        return self

__imul__

La méthode magique __imul__ définit comment doit se comporter l'opération de multiplication in-place. Lorsque vous utilisez l'opérateur *= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __imul__(self, other: 'MyNumber') -> 'MyNumber':
        """Multiplie la valeur de l'instance par la valeur de l'autre instance en place."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value *= other.value
        print(f'après *= : {self.value=}')
        return self

__itruediv__

La méthode magique __itruediv__ définit comment doit se comporter l'opération de division réelle in-place. Lorsque vous utilisez l'opérateur /= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __itruediv__(self, other: 'MyNumber') -> 'MyNumber':
        """Divise la valeur de l'instance par la valeur de l'autre instance en place."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value /= other.value
        print(f'après /= : {self.value=}')
        return self

__ifloordiv__

La méthode magique __ifloordiv__ définit comment doit se comporter l'opération de division entière in-place. Lorsque vous utilisez l'opérateur //= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __ifloordiv__(self, other: 'MyNumber') -> 'MyNumber':
        """Effectue une division entière en place de la valeur de l'instance par la valeur de l'autre instance."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value //= other.value
        print(f'après //= : {self.value=}')
        return self

__imod__

La méthode magique __imod__ définit comment doit se comporter l'opération de modulo in-place. Lorsque vous utilisez l'opérateur %= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __imod__(self, other: 'MyNumber') -> 'MyNumber':
        """Effectue une opération de modulo en place de la valeur de l'instance par la valeur de l'autre instance."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value %= other.value
        print(f'après %= : {self.value=}')
        return self

__ipow__

La méthode magique __ipow__ définit comment doit se comporter l'opération de puissance in-place. Lorsque vous utilisez l'opérateur **= sur des instances de votre classe, cette méthode est appelée.

    #... (code précédent dans number.py)

    def __ipow__(self, other: 'MyNumber') -> 'MyNumber':
        """Élève la valeur de l'instance à la puissance de la valeur de l'autre instance en place."""
        print(f'entrée : {self.value=}, {other.value=}')
        self.value **= other.value
        print(f'après **= : {self.value=}')
        return self

Exemple : Utilisation des opérations in-place

Maintenant que nous avons défini les opérateurs in-place pour notre classe MyNumber, voyons comment ils fonctionnent dans inplace_example2.py :

from number import MyNumber

## Crée un nouvel objet MyNumber
a = MyNumber(13)

## Utilise la méthode __iadd__
a += MyNumber(5)
## Sortie :
## entrée : self.value=13, other.value=5
## après += : self.value=18

## Utilise la méthode __isub__
a -= MyNumber(5)
## Sortie :
## entrée : self.value=18, other.value=5
## après -= : self.value=13

## Utilise la méthode __imul__
a *= MyNumber(5)
## Sortie :
## entrée : self.value=13, other.value=5
## après *= : self.value=65

## Utilise la méthode __itruediv__
a /= MyNumber(5)
## Sortie :
## entrée : self.value=65, other.value=5
## après /= : self.value=13.0

## Utilise la méthode __ifloordiv__
a //= MyNumber(2)
## Sortie :
## entrée : self.value=13.0, other.value=2
## après //= : self.value=6.0

## Utilise la méthode __imod__
a %= MyNumber(4)
## Sortie :
## entrée : self.value=6.0, other.value=4
## après %= : self.value=2.0

## Utilise la méthode __ipow__
a **= MyNumber(3)
## Sortie :
## entrée : self.value=2.0, other.value=3
## après **= : self.value=8.0

Ensuite, tapez la commande suivante dans le terminal pour exécuter le script.

python inplace_example2.py

Sommaire

Dans ce tutoriel, nous avons exploré les méthodes magiques Python liées aux opérations numériques, qui vous permettent de définir un comportement personnalisé pour vos classes lors de l'interaction avec différents types d'opérations numériques. Nous avons abordé les opérateurs unaire, les opérateurs binaire et les opérations in-place, en apprenant comment implémenter chaque méthode magique au fur et à mesure.

En implémentant ces méthodes magiques dans vos classes personnalisées, vous pouvez créer des objets intuitifs et faciles à utiliser qui fonctionnent de manière transparente avec les opérations standard de Python. Cela améliore non seulement la lisibilité de votre code, mais également sa maintenabilité et son ergonomie.

Au fur et à mesure que vous continuez à développer vos compétences en Python, envisagez d'expérimenter d'autres méthodes magiques pour personnaliser encore plus le comportement de vos classes et créer des abstractions encore plus puissantes.