Introduction à la Programmation Orientée Objet avec Python

Programmation Orientée Objet

Les interactions entre les objets

 

 

Introduction

Note

This translation was done by Gerard Labadie.

There is a Brazilian Portuguese translation of this tutorial (By Rodrigo Nishino) - Introdução Programacao Orientada Objetos.

The original (in English) is at - Introduction to Object Oriented Programming.

Cela fait maintenant plus de deux ans que je programme avec Python [1]. J'ai programmé de manière procédurale pendant huit ans précédemment , mais je n'étais pas habitué aux objets ou à la programmation orientée object (POO).

Toute la philosophie conceptuelle de Python encourage un style clair de programmation. Son typage faible et son système de nommage font qu'il est facile d'écrire du code élégant et modulaire [2].

Ces facteurs, et son unique "structure de block par indentation" font de Python un premier langage idéal. Malgré cela, j'ai eu du mal à apprendre les classes et les objets. Cela ressemblait à un obstacle important à passer, comme tout nouveau système.

En fait les principes de base de la POO sont assez faciles à apprendre. Comme d'autres aspects de Python ils sont intelligemment implémentés et bien pensés. Ce tutoriel est l'introduction à la POO que j'aurais aimé avoir à l'époque.

Cet article suppose une connaissance de base de la syntaxe Python. Si vous savez comment créer les types de données de base et appeler des fonctions, et que vous voulez en savoir plus sur les objets et classes, vous trouverez cet article utile.

Objets et POO

Objets et POO sont au centre de la manière Python fonctionne. Vous n'êtes pas obligé d'utiliser la POO dans vos programmes - mais comprendre le concept est essentiel pour devenir plus qu'un débutant. Entre autres raisons parce que vous aurez besoin d'utiliser les classes et objets fournis par la librairie standard.

Mais qu’est ce qu’un Objet ?

Qu'est-ce qu'un Objet ? C'est le fait de programmer - donc un objet est un concept. Le fait d'appeler des éléments de nos programmes objets est une métaphore - une manière utile de penser à propos d'eux.

En Python les éléments de base de la programmation sont des choses comme des chaînes de caractères (strings), des dictionnaires, des entiers, des fonctions, etc... [3] Ils sont tous des objets [4]. Cela signifie qu'ils peuvent avoir des choses en commun.

Avant de regarder de plus près ce que cela signifie, nous allons rapidement survoler quelques principes de base de programmation.

Programmation procédurale

Si vous avez déjà programmé, vous êtes habitué à la programmation procédurale. Cela consiste à diviser votre programme en blocs réutilisables appelés procédures ou fonctions [5].

Vous essayez autant que possible de garder votre code en blocs modulaires, en décidant de manière logique quel bloc est appelé. Cela demande moins d’effort pour visualiser ce que votre programme fait. Cela rend plus facile la maintenance de votre code – vous pouvez voir ce que fait une portion de code. Le fait d’améliorer une fonction (qui est réutilisée) peut améliorer la performance à plusieurs endroits dans votre programme.

Note

Pour un point de vue intéressant sur le développement de la programmation procédurale, lisez a mini history of programming.

Séparation des donnéés

La programmation procédurale maintient une séparation stricte entre votre code et vos données.

Vous avez des variables, qui contiennent vos données, et des procédures. Vous passez vos variables à vos procédures – qui agissent sur elles et peut-être les modifient.

Si une fonction veut modifier ce que contient une variable en la passant à une autre fonction, elle a besoin d’accéder à la variable et à la fonction qu’elle appelle. Si vous faites des opérations compliquées, cela peut être avec beaucoup de variables et de fonctions.

Entrez dans l’Objet

Il se trouve que beaucoup d’opérations sont communes à des objets du même type. Par exemple beaucoup de langages incluent une manière de créer une version en minuscule d’une chaîne de caractères.

Il y a beaucoup d’opérations standards qui sont associées uniquement à des chaînes de caractères. Par exemple passer cette chaîne en minuscule, en majuscule, découper la chaîne, etc…

Dans un langage orienté objet, nous construisons ces opérations en tant que propriété de l’objet chaîne de caractères. En Python nous appelons cela des méthodes [7].

Chaque objet d’une chaîne de caractères a un jeu de méthodes standards, vous en avez sans doute déjà utilisé certaines.

Par exemple :

original_string = ' un texte '

# enlever l’espace du début et de la fin
string1 = original_string.strip()

# passé en majuscule
string2 = string1.upper()
print string2
UN TEXTE

# teste si minuscule
string2.lower() == string1
True

Python utilise la dot syntax pour accéder aux attributs des objets. string2.lower() signifie “appelle la méthode lower de l’objet string2“ . Cette méthode renvoie une nouvelle chaîne de caractères - le résultat de l’appel de la méthode.

Donc chaque chaîne de caractères est en fait un objet chaîne de caractères – et possède toutes les méthodes d’un objet de type chaîne de caractères [8]. Dans la terminologie Python, toutes les chaînes de caractères sont des chaînes type.

Dans le modèle objet, les fonctions (méthodes) et autres attributs qui sont associés avec un type particulier d’objet deviennent une partie de l’objet. Les données et les fonctions pour manipuler les données ne sont plus séparées, mais sont liées ensemble dans un seul objet.

Créons de nouveaux Objets

Regardons un peu plus précisément ce qui se passe.

En Python il existe un objet de type chaîne de caractères blueprint appelé le string type. Son nom est en fait str. Il possède toutes les méthodes et propriétés associées avec la chaîne de caractères.

Chaque fois qu’une nouvelle chaîne de caractères est créée, le blueprint est utilisé pour créer un nouvel objet avec toutes les propriétés du blueprint. Tous les built in datatypes (types de données) ont leur propre 'blueprint' - les entiers (int), les flottants (float), les booléens (bool), les listes (list), les dictionnaires (dict), et ainsi de suite.

Pour ces built in datatypes, nous pouvons soit utiliser la syntaxe python normale Python pour les créer- ou nous pouvons utiliser le blueprint lui-même (le type).

# creation d’un dictionnaire de façon normale
a_dict = {
    'key' : 'value',
    'key2' : 'value2'
    }
# utilise 'dict' pour en créer un
list_of_tuples = [('key', 'value'),
                 ('key2', 'value2')]
a_dict_2 = dict(list_of_tuples)
#
print a_dict == a_dict_2
True
print type(a_dict)
<type 'dict'>
print type(a_dict_2)
<type 'dict'>

Voyez comment nous avons créé a_dict_2 en passant une liste de tuples à dict. Des choses élémentaires, mais qui illustrent que les nouveaux objets sont créés à partir des blueprints. Ces objets ont toutes les méthodes définies dans le blueprint.

Le nouvel objet est appelé une instance; et le fait de le créer est appelé instanciation (qui signifie créer une instance). Pour les built in datatypes le blueprint est connu en tant que type de l’ objet. Vous pouvez tester le type d’un objet avec la fonction type intégrée [9].

Cela peut sembler beaucoup à assimiler en une seule fois – mais cela ne fait sans doute appel qu’à des choses que vous avez déjà faites.

Nous avons déjà vu un exemple d’utilisation de la méthode string. Nous allons terminer cette section en utilisant des méthodes de type dictionnaire.

a_dict = {
    'key' : 'value',
    'key2' : 'value2'
    }
a_dict_2 = a_dict.copy()
print a_dict == a_dict_2
True
a_dict.clear()
print a_dict
{}
print a_dict.clear
<built-in method clear of dict object at 0x0012E540>
print type(a_dict.clear)
<type 'builtin_function_or_method'>

Ci-dessus nous avons utilisé la méthode clear de a_dict en appelant a_dict.clear(). Quand nous avons affiché clear, au lieu de l’appeler, nous voyons que c’est juste un autre objet. C’est un objet method du type approprié.

Les Fonctions sont des Objets

Juste pour démontrer que les fonctions sont des objets, je vais vous montrer une astuce à ce propos.

Avez-vous déjà écrit du code qui ressemble à cela ?

if value == 'one':
    # faire quelque chose
    function1()
elif value == 'two':
    # faire autre chose
    function2()
elif value == 'three':
    # faire autre chose
    function3()

Certains langages ont une commande appelée switch qui simplifie l’écriture de ce genre de code.

En Python nous pouvons arriver au même résultat (avec moins de lignes de code) en utilisant un dictionnaire de fonctions.

Dans cet exemple, supposons que nous avons trois fonctions. Vous voulez appeler une des functions, selon la valeur que prendra la variable appelée choice.

def function1():
    print 'You chose one.'
def  function2():
    print 'You chose two.'
def  function3():
    print 'You chose three.'
#
# switch est notre dictionnaire de functions
switch = {
    'one': function1,
    'two': function2,
    'three': function3,
    }
#
# le choix peut être 'one', 'two', or 'three'
choice = raw_input('Enter one, two, or three :')
#
# call one of the functions
try:
    result = switch[choice]
except KeyError:
    print 'I didn\'t understand your choice.'
else:
    result()

La magie est dans la ligne result = switch[choice]. switch[choice] qui renvoie un de nos objets fonctions (ou lève une KeyError). Ensuite nous exécutons result(), qui l’appelle.

Attention!

Vous pouvez économiser une ligne ou deux de code en faisant à la fin :

# appelle une des fonctions
try:
    switch[choice]()
except KeyError:
    print 'I didn\'t understand your choice.'

Cela appelle directement la fonction renvoyée par switch[choice]. Cependant, si cette fonction active KeyError (à cause d’un bug); elle sera attrapée par le block try...except. Cette erreur peut être très difficile à trouver, parce que la gestion d’erreur renvoie la mauvaise erreur.

En général vous devriez envelopper vos blocks try...except avec aussi peu de code que possible.

User Defined Classes

La véritable astuce est que nous pouvons créer nos propres blueprints. Ils s’appellent classes. Nous pouvons définir notre propre class d’objet – et à partir de cela créer autant d’instances de cette classe que nous le voulons. Toutes les instances seront différentes – en fonction des données fournies quand elles sont créées. Elles auront toutes les méthodes (et les autres propriétés) du blueprint - la classe.

Voyons avec un exemple simple. Nous définissons notre propre classe en utilisant le mot-clé class.

Les méthodes sont définies comme les fonctions – en utilisant le mot-clé def. Elles sont indentées pour montrer qu’elles sont à l’intérieur de la classe.

class OurClass(object):
    """Class docstring."""

    def __init__(self, arg1, arg2):
        """Method docstring."""
        self.arg1 = arg1
        self.arg2 = arg2

    def printargs(self):
        """Method docstring."""
        print self.arg1
        print self.arg2

Il y a sans doute des choses qui demandent des explications ici. Cela sera plus facile si vous voyez un exemple de cela en action.

instance = OurClass('arg1', 'arg2')
print type(instance)
<class 'OurClass'>
instance.printargs()
arg1
arg2

Dans cet exemple nous créons une instance de OurClass, et nous l’appelons instance. Quand nous la créons, nous passons arg1 et arg2 en arguments.

Quand nous appelons instance.printargs() les arguments de départ sont affichés.

L’héritage

La définition de la classe commence par :

class OurClass(object):

La définition de la classe permet ce qu’on appelle l’héritage. Cela signifie que votre classe hérite des propriétés d’une classe. Je ne vais pas expliquer cela pour le moment. Smile

Tout ce que vous devez savoir pour l’instant est - si vous n’héritez pas d’une autre classe alors vous devez hériter d’ object. Vos définitions de classe devraient ressembler à cela :

class ClassName(object):

La méthode __init__

La méthode __init__ (init pour initialise) est appelée quand l’objet est instancié. L’Instanciation est faite en appelant la classe.

De notre exemple :

instance = OurClass('arg1', 'arg2')

Ici une nouvelle instance est créée. Puis sa méthode __init__ est appelée et les arguments 'arg1' et 'arg2' lui sont passés.

Pour vraiment comprendre la méthode __init__ il faut comprendre self.

Le Paramètre self

Les arguments acceptés par la méthode __init__ (connu comme la méthode signature) sont :

def __init__(self, arg1, arg2):

mais nous lui passons en fait deux arguments :

instance = OurClass('arg1', 'arg2')

Que se passe-t-il, d’où vient l’argument en plus ?

Quand nous accédons les attributs de l’object, nous le faisons par nom (ou par référence). Ici instance est une référence vers notre nouvel objet. Nous accédons la méthode printargs de l’objet instance en utilisant instance.printargs.

Pour accéder les attributs de l’objet à partir de la méthode __init__ nous avons besoin d’une référence vers l’objet.

Quand une méthode est appelée, une référence vers l’objet principal est passée en tant que premier argument. Par convention le premier argument appelé par les méthodes est toujours self.

Cela signifie que dans la méthode __init__ nous pouvons faire :

self.arg1 = arg1
self.arg2 = arg2

Ici nous donnons des valeurs aux attributs sur l’objet. Vous pouvez vérifier cela avec :

instance = OurClass('arg1', 'arg2')
print instance.arg1
arg1

Des valeurs comme cela sont connues comme des attributs de l’objet. Ici la méthode __init__ définit les attributs arg1 et arg2 de l’instance.

printargs

Nous en savons maintenant assez pour comprendre ce qui se passe dans la méthode printargs.

Cette méthode ne prend pas d’arguments, donc quand on la définit, on a uniquement besoin d’indiquer le paramètre self qui est toujours passé aux méthodes de l’objet.

def printargs(self):

Quand cette méthode est appelée elle cherche (et affiche) les arguments d’origine qui étaient sauvegardés en tant qu’attributs de l’objet par __init__.

Astuce

Précisons les termes utilisés.

Les 'fonctions' qui font partie de l’objet sont appelées méthodes.

Les valeurs sont appelées 'attributs'.

Vous pouvez examiner toutes les méthodes et attributs associés à un objet avec la commande dir :

print dir(some_obj)

La puissance des Objets

Comme vous pouvez le voir, les objets combinent les données et les méthodes manipulant ces données. Cela signifie qu’il est possible de manipuler des choses compliquées - mais leur présenter une interface simple. La manière dont ces opérations sont exécutées à l’intérieur de l’objet devient juste un détail de l’implémentation. Toute personne utilisant l’objet a juste besoin de connaître les méthodes publiques et les attributs. Ceci est le véritable principe de l’encapsulation. D’autres parties de votre application (ou même d’autres programmeurs) peuvent utiliser vos classes et leurs méthodes publiques – mais vous pouvez mettre à jour l’objet sans rendre inutilisable l’interface qu’ils utilisent.

Vous pouvez également passer des objets au lieu de juste passer des données. Cela est l’un des aspects les plus utiles de la programmation orientée objet. A partir du moment où vous avez une référence vers l’objet vous pouvez accéder n’importe quel attribut de l’objet. Si vous voulez faire des opérations compliquées dans une partie de votre programme, vous pouvez sans doute le faire avec des procédures et des variables. Vous pouvez soit avoir besoin d’utiliser plusieurs variables globales pour stocker un état (qui sont plus longues à accéder que des variables globales et pas adapatées si un module a besoin d’être re-utilisé dans votre application) – ou vos procédures peuvent avoir besoin de passer beaucoup de variables.

Si vous implémentez une simple classe qui a un grand nombre d’attributs représentant l’état de votre application, vous avez juste besoin de passer une référence vers cet objet. N’importe quelle partie de votre code qui accède l’objet accèdera aussi ses attributs.

Le principal avantage de l’objet est que c’est une image utile. Cela correspond à la manière dont nous penson. Dans la vie les objets ont des propriétés et inter-agissent les uns avec les autres. Si notre langage de programmation correspond à notre manière de penser, il sera plus facile de l’utiliser pour penser de manière creative.

Sujets Avancés

Nous avons seulement traité les bases dans ce tutoriel. Heureusement vous en savez maintenant assez pour créer et utiliser vos propres classes. Il reste beaucoup de choses à découvrir. Parmi les sujets que je pourrai développer pour compléter ce tutoriel il y a :

  • L’héritage
  • class attributes
  • __dict__
  • subclassing built in types
  • __new__
  • __getattr__ and __setattr__
  • attributes privés (simple et double underscore)
  • classmethods et staticmethods

Pied de Pages

[1]Cela faisait deux ans en Juin 2005.... J’ai commencé à apprendre Python pour un projet appelé atlantibots.
[2]Le système de modules et packages de Python également – ils ont un rapport avec le système de noms, et les autres langages l’envient.
[3]Ce sont les datatypes de base – et beaucoup d’autres choses comme les classes, instances, et méthodes que nous rencontrerons bientôt dans cette introduction.
[4]Comme Smalltalk – dont on parle parfois comme l’archétype du langage orienté objet.
[5]La différence était qu’une fonction renvoie une valeur mais pas une procédure. Maintenant on a te ndance à tout appeler fonctions. Nous ne l’appelons pas programmation fonctionnelle cependant, c’est quelque chose d’autre.
[6]Ou est-ce pour protéger le programmeur vis-à-vis du code ?
[7]Le 'vieux' nom orienté objet pour cela est le ‘passage de message'. Maintenant ce n’est plus une métaphore utile.
[8]Vous pouvez voir un liste de méthodes « chaîne de caractères « à string methods.
[9]En fait type n’est pas une fonction – c’est un type. C’est le type de types (type(type) is type). Nous pouvons cependant l’utiliser en tant que fonction.
[10]Essayez import this avec un prompt d’interpréteur interactif. Ceci est appelé le Zen de Python.

For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store.

Hosted by Webfaction

Return to Top

Page rendered with rest2web the Site Builder

Last edited Tue Aug 2 00:51:34 2011.

Counter...