P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ EXPERIENCE IS SOMETHING YOU DON'T GET UNTIL JUST AFTER YOU NEED IT Articles | Connexion
 
~Les opérateurs en Python

Précédent  
  Python  
  Suivant
 Présentation

Nous abordons enfin la dernière partie consacrée au langage Python lui-même.
 Sommaire


 Introduction

Durant les explications proposées le mois dernier, nous avons pu nous initier brièvement à la surcharge d'opérateurs. Fidèle à sa simplicité, surcharger un opérateur au sein d'une classe ne demande en Python que l'écriture d'une nouvelle méthode possédant un nom bien particulier. De manière générale, tous les opérateurs arithmétiques ou logiques (*, /, ==, <, >.) correspondent à une méthode intitulée de la sorte : __operateur__(self[, params])


 Dériver un entier

Pour bien comprendre le principe de la surcharge des opérateurs, dériver le type entier (int en Python) constitue un bon point de départ. Notre classe, fort simple, offrira des comportements constants pour certaines opérations. Ainsi, a * b renverra toujours 99. La classe que nous créons se nomme Nbr et se définit de la sorte :

class Nbr(int):
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
La mention entre parenthèses indique que nous héritons de la classe int. Souvenez-vous que Python se veut un langage entièrement objet. Ainsi, même les types dits "primitifs" (entiers, réels.) s'avèrent en réalité des instances de classes. Pour cette première étape, la définition d'un nouveau constructeur ne s'avèrera pas nécessaire, Python se chargeant de tout. La classe ainsi définie surchagera trois types d'opérateurs : deux opérateurs arithmétiques (multiplication et division), un opérateur logique (supérieur à) et un opérateur de transtypage (float).

Un opérateur possède une ou deux opérandes. Cela signifie que les méthodes acceptent zéro ou un paramètre puisque le premier opérande correspond à l'instance self. Voyons l'exemple de la multiplication :

class Nbr(int):
  def __mul__(self, y):
    return Nbr(99)
       
      
JextCopier dans Jext
L'opérateur de multiplication correspond à la méthode __mul__(). L'implémentation proposée retourne toujours la valeur 99. Nous aurions pu envisager la réalisation d'un opérateur au comportement normal. En ce cas, voici ce que nous aurions rédigé :

return Nbr(int.__mul__(self, y))
       
      
JextCopier dans Jext
Le code source présent sur le CD-Rom présente l'implémentation de la division, qui retourne toujours -99. L'encadré numéro un résume les opérateurs arithmétiques principaux. Les opérateurs tels que la multiplication ou la soustraction sont définis en positionnant la valeur self comme premier opérande. Lorsque ces opérations ne sont pas commutatives, on peut utiliser les right-opérateurs. Il suffit de préfixer le nom des opérateurs par le caractère "r" pour employer self comme seconde opérande. Ainsi, __rmul__(self, y) correspond à l'opération y * x.

Les opérateurs logiques permettent les comparaisons entre objets. La valeur de retour sera vraie pour toute valeur différente de zéro ou de None. Imaginons l'implémentation suivante pour notre classe Nbr :

def __gt__(self, y) :
  return 0
       
      
JextCopier dans Jext
L'opérateur ici surchargé se trouve ">". Notre code source implique que toute comparaison de ce type sera fausse. Ainsi a > b est faux, mais b > a est faux également ! Vous trouverez dans l'encadré numéro deux les opérateurs logiques les plus communs. Un opérateur non présent dans la liste se nomme __cmp__. Ce dernier se lie à la fonction cmp(x, y) dont la valeur de retour se veut -1 si x < y, 0 pour x = y et 1 si x > y. Notez que la classe int implémente cette seule méthode.

La dernière méthode proposée par la classe Nbr concerne le transtypage vers un réel de type flottant. Les méthodes nommées __typeprimitif__ servent à proposer des implémentations de transtypage.

def __float__(self):
  return int.__float__(self) / 2
       
      
JextCopier dans Jext
L'instruction placée au coeur de la méthode permet de renvoyer la moitié du nombre sous forme de flottant. Ainsi le code suivant affichera 3.5 :

a = Nbr(7)
print float(a)
       
      
JextCopier dans Jext
Opération de transtypage un peu particulière vous en conviendrez.


 Nombres complexes

Bien que Python possède sa propre implémentation native des nombres complexes (par l'intermédiaire de la fonction complex(r, i) notamment), ces nombres se révèlent intéressants pour étudier la création d'opérateurs pour une classe. Jusqu'à présent, nous nous étions contenter de surcharger les opérateurs. Nous allons dorénavant créer les nôtres. Un nombre complexe, sous sa forme cartésienne, possède deux attributs : une partie réelle et une partie imaginaire. Le listing numéro un offre un aperçu d'une classe Python incarnant un nombre complexe. Le code ne s'avère pas complet car certaines fonctions importantes, comme la division ou l'élévation à la puissance, manquent. Pour programmer de telles fonctions, l'emploi de la représentation polaire des nombres complexes (avec un module et un argument - ou magnitude et angle) simplifierait les opérations.

La classe présentée dans le listing numéro un offre les opérateurs d'addition, de soustraction et de multiplication. En outre, l'opérateur de transtypage vers une chaîne de caractères se trouve inscrit dans le code. Ceci nous permet d'afficher notre nombre sous la forme "(réel, imaginaire)" par l'entremise de l'instruction "print objetComplex".

Les quelques lignes extrêmement simples de code présentées octroient l'opportunité de rédiger le programme suivant :

c1 = Complex(1, 2)
c2 = Complex(3, 8)
print (c1 + c2)
print (c1 * c2)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Le résultat des opérations arithmétiques correspondant à une instance de la classe Complex, l'affichage de print se verra correctement traduit grâce à notre méthode __str__.

Vous pouvez découvrir la nature des autres opérateurs proposés par Python en employant astucieusement la fonction dir(objet). Dans un shell Python, exécutez les commandes suivantes :

dir(10)
dir('hello')
       
      
JextCopier dans Jext
Une liste de méthodes apparaîtra alors. Parmi celles-ci vous pourrez aisément dégager les méthodes correspondant aux opérateurs. Les conditions d'utilisation et d'implémentation des méthodes vous seront fournies par l'instruction suivante dans un shell Python :

print objet.__operateur__.__doc__
       
      
JextCopier dans Jext
La mention "10.__xor__.__doc__" provoquera par exemple l'affichage de l'utilisation de l'opérateur "ou exclusif".

Note 1
__mul__ : x * y
__div__ : x / y
__mod__ : x % y
__add__ : x + y
__sub__ : x - y
__neg__ : -x
__pow__ : x ^ y
__invert__ : 1 / x

Note 2
__eq__ : a == b
__ne__ : a != b
__gt__ : a > b
__ge __ : a >= b
__lt__ : a < b
__le__ : a <= b
__and__ : a and b
__or__ : a or b
__not__ : not a

class Complex:
    """Un nombre complexe"""

    def __init__(self, real, img):
        """Complex(réel, imaginaire)"""
        self.real = real
        self.img = img

    def __add__(self, c):
        """Addition"""
        return Complex(self.real + c.real, self.img + c.img)

    def __sub__(self, c):
        """Soustraction"""
        return Complex(self.real - c.real, self.img - c.img)

    def __mul__(self, c):
        """Multiplication"""
        return Complex(self.real * c.real - self.img  * c.img, \
                       self.img  * c.real + self.real * c.img)

    def __str__(self):
        """Représentation sous forme de chaîne de caractères"""
        return "(" + str(self.real) + ", " + str(self.img) + ")"
       
      
JextCopier dans Jext


par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Python  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE