Le guide ultime et définitif sur la programmation orientée objet en Python à l’usage des débutants qui sont rassurés par les textes détaillés qui prennent le temps de tout expliquer. Partie 2.


Prérequis pour cette partie :

Rappel

On créé des objets en instanciant des classes. Les classes sont des plans décrivant les objets, et notamment déclarant leurs méthodes (le comportement) et leurs attributs (les données) :

class ArticleDeSamEtMax:
 
    def __init__(self, titre):
 
        self.titre = titre
 
 
>>> article = ArticleDeSamEtMax('Votre Python aime les pip')
>>> print article.titre
Votre Python aime les pip'

Les méthodes nommées __methode__ (avec deux underscores de chaque côté) sont appelées automatiquement dans certaines conditions. __init__ est appelée automatiquement après la création de l’objet, et on s’en sert pour créer l’état de départ de l’objet (initialiser).

La chose la plus difficile à comprendre au début est self, l’objet en cours. Pour nous aider un peu, utilisons la fonction id():

>>> id("bip")
22114176
>>> id(13.2)
22417496
>>> id({})
20801584
>>> id(article)
21427120

id() retourne un identifiant unique pour chaque objet. Imaginez le comme “l’adresse en mémoire” d’un l’objet.

Maintenant créons une méthode qui identifie un peu self :

class ArticleDeSamEtMax:
 
    def __init__(self, titre):
        self.titre = titre
 
    def print_self(self):
 
        print self.titre
        print self
        print id(self)
 
>>> article1 = ArticleDeSamEtMax("La théorie de la salle de bain")
>>> article2 = ArticleDeSamEtMax("Mieux de fesse que de face")

Si j’affiche le titre et les id de l’article 1, ça donne ça :

>>> print article1.titre
La théorie de la salle de bain
>>> print article1
<__main__.ArticleDeSamEtMax instance at 0x1635488>
>>> print id(article1)
23286920

Et regardez le print_self :

>>> article1.print_self()
La théorie de la salle de bain
<__main__.ArticleDeSamEtMax instance at 0x1635488>
23286920

C’est exactement la même chose ! self EST article1 puisque self est l’objet en cours.

Pareil pour l’article2:

>>> print article2.titre
Mieux de fesse que de face
>>> print article2
<__main__.ArticleDeSamEtMax instance at 0x16355a8>
>>> print id(article2)
23287208
>>> article2.print_self()
Mieux de fesse que de face
<__main__.ArticleDeSamEtMax instance at 0x16355a8>
23287208

C’est cela la notion de l’objet en cours. Python passe automatiquement l’objet à lui-même, de telle sorte que chaque méthode ait une référence à lui-même pour pouvoir lire et modifier ses propres attributs.

Maintenant on en fait quoi ?

La POO a pour principal attrait de permettre de proposer une belle API, c’est à dire de créer du code réutilisable et facile à manipuler. Comme je vous le disais précédemment, il n’y a rien qu’on puisse faire en POO qu’on ne puisse faire autrement. On va surtout l’utiliser pour donner un style au code.

En fait, on fait de la POO pour celui qui va utiliser votre code plus tard.

Amusons-nous un peu avec le Web online de l’Internet

Imaginez, vous voulez proposer une bibliothèque qui permettent de récupérer les 100 dernières questions postées à propos de Python sur le site Stackoverflow. Heureusement Stackoverflow est très ouvert, et propose même d’avoir accès à leur base de données (avec quelques jours de retard) assez directement depuis data.stackexchange.com.

Premièrement, il faut créer un export de la base de données de Stackoverflow. Ca ne fait pas partie de la POO, alors je vous le donne tout fait.

Cet export peut être récupéré au format CSV, du coup on pourra télécharger nos données sous cette forme :

CreationDate,Post Link
"2013-01-13 12:42:41","14303571"
"2013-01-13 12:40:12","14303548"
"2013-01-13 12:35:47","14303498"
...

Pour lire les données et les récupérer, pas besoin de POO, un petit script Python procédural fait très bien le taff :

import csv
import urllib2
 
from io import StringIO
 
# l'URL du CSV
URL = "http://data.stackexchange.com/StackOverflow/csv/109782"
 
# on télécharge les données, on les décode et on les enrobe dans
# StringIO pour qu'elles soient lisible de la même manière qu'un fichier
# malgré le fait qu'elles soient juste en mémoire
csv_data = StringIO(urllib2.urlopen(URL).read(100000).decode('utf8'))
 
 
# on utilise le module CSV pour lire notre "fichier" CSV
# DictReader retourne une liste de dictionnaires, un pour chaque entrée du "fichier"
for question in csv.DictReader(csv_data):
 
    # et on a accès aux données de chaque question
    print question['CreationDate'] # afficher la date de création
    print question['Post Link'] # afficher l'id de la question

C’est bien, c’est facile et ça marche :

2013-01-13 12:42:41
14303571
2013-01-13 12:40:12
14303548
...

Astuce au passage : si vous ouvrez le CSV issu de data.stackexchange.com, vous noterez qu’il y a plein de petits détails qui rendent ce format pas toujours facile à parser. Plutôt que d’y aller comme un bourrin avec des split(), nous utilisons donc le module csv pour récupérer chaque entrée du fichier comme un dictionnaire qui aura la structure:

{'nom_de_colonne': 'valeur_pour_cette_ligne', ...}

On est cependant loin d’avoir une bibliothèque réutilisable. On peut commencer à faire une fonction réutilisable :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
 
import csv
import json
import urllib2
 
from datetime import datetime
from io import StringIO
 
 
DATA_SOURCE_URL = "http://data.stackexchange.com/StackOverflow/csv/109782"
QUESTION_URL = "http://stackoverflow.com/questions/{id}"
 
 
def download_questions(url=DATA_SOURCE_URL):
 
    csv_data = StringIO(urllib2.urlopen(url).read(100000).decode('utf8'))
 
    for question in csv.DictReader(csv_data):
 
        # on transforme la chaîne date en objet datetime
        question['CreationDate'] = datetime.strptime(question['CreationDate'],
                                                     '%Y-%m-%d %H:%M:%S')
 
        # le deuxième champ est au format JSON
        # alors on le transforme en objet Python
        question['Post Link'] = json.loads(question['Post Link'])
 
        # on ajoute l'url de la question générée à partir de l'ID
        question['Post Link'] = QUESTION_URL.format(id=question['Post Link'])
 
        yield question

On pourrait l’importer et faire :

for question in download_questions():
    # affiche le titre et l'url
    print "{CreationDate} : {Post Link}".format(**question)

On obtient l’affichage suivant :

2013-01-13 12:42:41 : http://stackoverflow.com/questions/14303571
2013-01-13 12:40:12 : http://stackoverflow.com/questions/14303548
...

Et ce n’est pas une mauvaise façon de faire. On peut également faire des interfaces très sympas en programmation fonctionnelle, mais ce n’est pas le but de l’article.

Voyons comment on ferait ça en POO

Si on utilise uniquement les outils qu’on a vu jusqu’ici, voici ce qu’on obtient :

import csv
import json
import urllib2
 
from datetime import datetime
from io import StringIO
 
 
DATA_SOURCE_URL = "http://data.stackexchange.com/StackOverflow/csv/109782"
QUESTION_URL = "http://stackoverflow.com/questions/{id}"
 
 
# au crée un objet question dans lequel on va mettre les données de notre dico
 
class Question:
 
    def __init__(self, id, creation_date):
 
        # self est le fameux "object en cours", on lui attache les attributs
        self.id = id
        self.creation_date = creation_date
 
    # on va générer ces valeurs à la lecture, pas à l'écriture comme
    # tout à l'heure
    def get_creation_date(self):
        return datetime.strptime(self.creation_date, '%Y-%m-%d %H:%M:%S')
 
    def get_url(self):
        return QUESTION_URL.format(id=self.id)
 
 
# maintenant notre fonction nous retourne des objets Question
 
def download_questions(url=DATA_SOURCE_URL):
 
        csv_data = StringIO(urllib2.urlopen(url).read(100000).decode('utf8'))
 
        for question in csv.DictReader(csv_data):
 
            question['Post Link'] = json.loads(question['Post Link'])
 
            # au lieu de retourner des dictionaires, on retourne
            # des objets Question
            yield Question(creation_date=question['CreationDate'],
                           id=question['Post Link'])

Donc on a encore une fonction, sauf que cette fois elle ne sort plus des dicos, mais des objets Question. A première vue ça n’a pas l’air très intéressant. Ca fait la même chose, mais c’est plus long.

Par contre on a déjà un petit changement du côté de l’utilisation, et c’est ça qu’on vise :

for question in download_questions():
    # affiche le titre et l'url
    print "{creation_date} : {url}".format(creation_date=question.creation_date, 
                                           url=question.get_url())

Au lieu d’avoir un dictionnaire qui pourrait contenir n’importe quoi, on a un objet question, avec un titre, et la possibilité de construire l’URL.

Petit détour par les attributs de classe

La POO, ce n’est pas juste faire des objets, c’est aussi les habiller.

Dans notre cas on a moitié fonction, moitié classe. On a des variables globales qui trainent. C’est pas terrible. On pourrait arranger ça avec des attributs de classe. Retour à un peu de théorie.

Un attribut de classe est un attribut qui appartient, non pas à l’objet, mais à la classe.

Par exemple:

class UnObjetQuiPassaitParLa:
 
    un_attribut_de_classe = 'meme valeur pour pour tous les objets'
 
    def __init__(self):
 
        self.attribut_d_objet = "valable seulement pour l'objet en cours"

un_attribut_de_classe est accessible depuis UnObjetQuiPassaitParLa, sans créer d’instance, donc sans avoir à faire UnObjetQuiPassaitParLa().

>>> print UnObjetQuiPassaitParLa.un_attribut_de_classe
meme valeur pour tous les objets et la classe
>>> print UnObjetQuiPassaitParLa.attribut_d_objet
 
Traceback (most recent call last):
  File "<pyshell#1>", line 11, in <module>
    print UnObjetQuiPassaitParLa.attribut_d_objet
AttributeError: class UnObjetQuiPassaitParLa has no attribute 'attribut_d_objet'

Par contre attribut_d_objet n’est pas accessible si on a pas d’instance.

Une instance a accès aux deux:

>>> print instance.un_attribut_de_classe
meme valeur pour tous les objets et la classe
>>> print instance.attribut_d_objet
valable seulement pour l'objet en cours

Les attributs de classe ont d’autres propriétés intéressantes, mais on va s’arrêter là pour le moment.

On peut aussi créer des méthodes de classe. C’est le même principe:

>>> class UnObjetQuiPassaitParLa:
...
...     @classmethod # <- tranforme la méthode en méthode de classe
...     def methode_de_classe(cls):
...
...         print "yeah baby"
...
>>> UnObjetQuiPassaitParLa.methode_de_classe()
yeah baby

On utilise ici un décorateur, qui dit que la méthode est une méthode de classe, donc accessible sans créer aucune instance.

Notez que la convention de nommage change : le premier argument n’est plus nommé self mais cls. C’est parce que le premier argument sera “la classe en cours” (UnObjetQuiPassaitParLa) et non plus “l’object en cours” (une instance de UnObjetQuiPassaitParLa).

Bon, tout ça c’est très flou pour le moment. Appliquons.

Little Boxes on the hill side

L’interêt de ça, c’est que ça va nous permettre de regrouper tout ce qui a un rapport avec notre objet Question dans la classe.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
 
import csv
import json
import urllib2
 
from datetime import datetime
from io import StringIO
 
 
class Question:
 
    # ces constantes sont maintenant des attributs de classe
    DATA_SOURCE_URL = "http://data.stackexchange.com/StackOverflow/csv/109782"
    QUESTION_URL = "http://stackoverflow.com/questions/{id}"
 
    def __init__(self, id, creation_date):
 
        # self est le fameux "object en cours", on lui attache les attributs
        self.id = id
        self.creation_date = creation_date
 
    def get_creation_date(self):
        return datetime.strptime(self.creation_date, '%Y-%m-%d %H:%M:%S')
 
    def get_url(self):
        return self.QUESTION_URL.format(id=self.id)
 
    # la fonction qui fabrique tous les objets Question est maintenant
    # à l'intérieur de la class Question, en tant que méthode de classe
    @classmethod
    def query(cls, url=DATA_SOURCE_URL):
 
            csv_data = StringIO(urllib2.urlopen(url).read(100000).decode('utf8'))
 
            for question in csv.DictReader(csv_data):
 
                question['Post Link'] = json.loads(question['Post Link'])
 
                yield Question(creation_date=question['CreationDate'],
                               id=question['Post Link'])

C’est ce qu’on appelle l’encapsulation. On fout tous les trucs qui ont un rapport entre eux dans la même boîte, et on laisse la boîte s’occuper de comment ça marche en interne.

Et là on commence à avoir une API très mignone :

print "Questions from : {}".format(Question.DATA_SOURCE_URL)
 
for question in Question.query():
    # affiche le titre et l'url
    print "{creation_date} : {url}".format(creation_date=question.creation_date, 
                                           url=question.get_url())

Tout part de l’objet Question. Si on cherche quelque chose liée à l’objet Question, on doit faire Question.< un_truc >. On peut expérimenter dans le shell avec la complétion du code. En regardant ce bout de code, pas besoin de savoir comment Question marche pour savoir ce que ça fait. C’est assez explicite.

On peut encore faire un peu mieux.

Les properties

Les propriétés, ou properties dans la langue de Cameron Diaz, sont des outils qui déguisent des méthodes pour les faire passer pour des attributs. L’exemple le plus simple est le suivant :

>>> class UnObjetQuiPassaitParLa:
...
...     def __init__(self, valeur):
...         self.valeur = valeur
...
...
...     def get_valeur_au_carre(self):
...
...         return self.valeur * self.valeur
...
>>> objet = UnObjetQuiPassaitParLa(2)
>>> print objet.get_valeur_au_carre()
4

C’est moche et verbeux. Avec une property, on dit à Python “fait comme si cette méthode était un banal attribut” :

>>> class UnObjetQuiPassaitParLa:
...
...     def __init__(self, valeur):
...         self.valeur = valeur
...
...     @property # <- tranforme la méthode en propriété
...     def carre(self):
...
...         return self.valeur * self.valeur
...
>>> objet = UnObjetQuiPassaitParLa(2)
>>> print objet.carre
4

Le décorateur @property transforme la méthode carre() et on peut l’utiliser sans parenthèse.

Appliquons cela à notre exemple :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
 
import csv
import json
import urllib2
 
from datetime import datetime
from io import StringIO
 
 
class Question:
 
    # ces constantes sont maintenant des attributs de classe
    DATA_SOURCE_URL = "http://data.stackexchange.com/StackOverflow/csv/109782"
    QUESTION_URL = "http://stackoverflow.com/questions/{id}"
 
    def __init__(self, id, creation_date):
 
        # self est le fameux "object en cours", on lui attache les attributs
        self.id = id
        self.creation_date = creation_date
 
    # on donne aux méthodes des noms plus simples, et on applique
    # le décorateur @property dessus
 
    @property
    def created(self):
        return datetime.strptime(self.creation_date, '%Y-%m-%d %H:%M:%S')
 
    @property
    def url(self):
        return self.QUESTION_URL.format(id=self.id)
 
    # la fonction qui fabrique tous les objets Question est maintenant
    # à l'intérieur de la class Question, en tant que méthode de classe
    @classmethod
    def query(cls, url=DATA_SOURCE_URL):
 
            csv_data = StringIO(urllib2.urlopen(url).read(100000).decode('utf8'))
 
            for question in csv.DictReader(csv_data):
 
                question['Post Link'] = json.loads(question['Post Link'])
 
                yield Question(creation_date=question['CreationDate'],
                               id=question['Post Link'])

Ca ne change pas grand chose, mais on peut virer le très moche get_url().

print "Questions from : {}".format(Question.DATA_SOURCE_URL)
 
for question in Question.query():
    # affiche le titre et l'url
    print "{creation_date} : {url}".format(creation_date=question.creation_date, 
                                           url=question.url)

Conclusion très provisoire

On a amélioré notre compréhension de l’usage de la POO dans le monde réel. Le code se complexifie côté bibliothèque. Par contre côté utilisateur final, on passe de ça :

 
from question_lib import download_questions, DATA_SOURCE_URL
 
print "Questions from : {}".format(DATA_SOURCE_URL)
 
for question in download_questions():
    print "{creationDate} : {url}".format(**question['Post Link'])

A :

 
from question_lib import Question
 
print "Questions from : {}".format(Question.DATA_SOURCE_URL)
 
for question in Question.query():
    # affiche le titre et l'url
    print "{creation_date} : {url}".format(creation_date=question.creation_date,
                                           url=question.url)

Le style change, la manière dont sont exposées les données n’est pas la même. Sur cette exemple simple, la complexité ajoutée pour le résultat fait que le jeu peut ne pas en valoir la chandelle. Sur des codes très gros, cela peut changer la vie.

path.py est un très bel exemple de cela : l’API est belle et simple (beaucoup plus que ce que propose la lib standard pour faire la même chose). Cela valait la complexité du code source pour obtenir ce résultat.

Un autre avantage de l’encapsulation, c’est que même si demain Stackoverflow change son format de données (ce n’est plus un CSV mais un XML par exemple), on a juste a apdater la classe. Le code qui utilise classe, lui, n’aura pas besoin de changer. Une bonne encapsulation (qu’on peut faire sans POO, mais la POO est spécialisée pour ça), aide les utilisateurs finaux de votre lib car ils savent que leur code ne devrait pas trop changer.

On ne va bien entendu pas s’arrêter là, car on peut faire beaucoup mieux. Ce n’était qu’un avant goût pédagoqique. Sautez à la partie 3.

35 thoughts on “Le guide ultime et définitif sur la programmation orientée objet en Python à l’usage des débutants qui sont rassurés par les textes détaillés qui prennent le temps de tout expliquer. Partie 2.

  • Sam Post author

    Je vois ce que tu veux dire. Les prérequis sont arbitraires vu que les gens n’apprennent pas dans un ordre précis, et yield ou la POO ne sont pas interdépendants.

    Je pense que l’important n’est pas que tout le monde puisse cash lire la partie, mais que tout le monde puisse au final le faire en lisant les ressources proposées. Il est illusoire de vouloir enseigner des choses complexes sans prérequis de toute façon.

    J’ai fais ce choix pour ne pas avoir un code tout pourri qui ne fais rien. La partie 1 fait déjà rien.

    Pour les fautes, j’attends que foxmask passe par là :-p

  • foxmask

    pas eu le temps de lire le 1° volet :) mais j’ai mis en branle l’idée de faire un iftt like donc ca va servir ;) surtout le yield !

  • Kontre

    J’ai vu deux fautes en survolant :
    On créer des objets => On crée des objets
    avec deux underscores de chaque côtés => avec deux underscores de chaque côté

    Cette partie n’est plus trop pour débutant je trouve, vu le tas de prérequis nécessaires. Mais je ne suis pas objectif, comme je connais le principe de la POO depuis assez longtemps, ça me parait plus simple que yield ou les décorateurs !

  • roro

    Je propose de changer le titre pour:
    “Petite ballade en dragster dans le vent frais du matin”
    Prérequis:
    – Combinaison préssurisée
    – Lunettes fumées
    – Camescope 360°
    – Nautamine
    – Assurance

  • Sam Post author

    Comment mon commentaire (qui était une réponse à Kontre) s’est retrouvé en haut ?

  • Heliomaster

    Salut à tous, je découvre le site au détour d’une question recherché dans “gougle” prononcé à la française :-)
    Et je me délecte à chaque article. Bravo pour le fond, j’apprends plein de choses étant totalement débutant en python, bravo pour la forme ou enfin je me rend compte que l’on peut parler programmation sans être chiant. En tout cas vous avez ici un nouvel aficionado du site.
    Félicitations et surtout,surtout…continuez

  • roro

    Hé ban, tu t’en pose des questions,toi..
    T’inquiète, on trie…

  • Kontre

    Y’a des paradoxes spatio-temporels sur ce site !

    Du coup on dirait que foxmask répond à Sam ^^

  • Mentat

    Très très bien Sam !
    J’ai enfin compris à quoi pouvait servir @classmethod (jamais utilisé !).

    Par contre, j’ai une question. A quoi sert l’argument title dans
    QUESTION_URL.format(title=self.title, id=self.id)
    Alors que QUESTION_URL = “http://stackoverflow.com/questions/{id}”
    J’ai loupé quelque chose ? Ou t’as pas enlevé tes mouffles ?

  • JEEK

    Prérequis + rappel ; p’tain on sent qu’il y a du “formateur inside” hein !

    Y a qu’un seul truc qui cloche avec cet article…
    …c’est que j’ai plus de chocolat chaud (mais pour ça, je vais survivre ; sinon c’est comme d’hab…nickel quoi) !
    ;-)

  • Etienne

    La méthode de classe qui fait le boulot et renvoie un instance d’elle-même… que dis-je, ‘yield’ des instances d’elle-même… Super! Illumination architecturale!

    Question: pourquoi ne pas calculer les valeurs renvoyées par les méthodes ‘url’ et ‘created’ avant l’instanciation, les passer lors de l’instanciation et y accéder directement en tant qu’attributs?

  • Sam Post author

    Les deux sont valables, ça dépend de comment tu veux répartir la charge du programme : tout en gros au chargement, ou au fur et à mesure de la lecture. Combien de mémoire on veut utiliser (car stocker prend de la mémoire, mais calculer non).

    Dans notre cas, ça a peut d’importance car le programme est simple. Sur des opérations lourdes, il faut réfléchir à l’usage du programme. C’est une choix technique.

    Une alternative est de calculer la première fois qu’on lit l’attribut, et de stocker le résultat pour les relectures futures.

  • gloubidabu

    Tout à la fin de Amusons-nous un peu avec le Web de l’Internet Online, juste après “On obtient l’affichage suivant” il y a 5 lignes qui squatent du genre “How to convert float point to hex representation according IEEE754…” mais on ne voit pas le fameux affichage suivant. Bug ?

    Dans la partie Properties la première classe est nommée UnTruc alors qu’elle est invoquée juste après avec UnObjetQuiPassaitParLa().

    Sinon bravo, c’est un vrai régal ces articles, j’en veux encore :)

  • Sam Post author

    Ce texte, c’est ce qu’affiche le programme dans la console. La deuxième erreur est en effet une typo bien grasse que je m’empresse de corriger.

  • foxmask

    me revoilou avec une remarque sur le bout de code

    .format(**question)

    il y a fort à parier que tout le monde n’a probablement pas saisi **question voire même .format(**question). Comme ce n’est pas dans les prérequis de départ ;)
    Dans ma série “de php a python” j’ai un article là dessus à paraitre ;)

  • Sam Post author

    C’est vrai. Je pourrais le rendre explicite, ce serait plus pédagogique.

  • Sam Post author

    Ouuuuuuuuu, mais non, c’est dans les prérequis. Car dans les pré-requis il faut avoir lu la partie 1, et dans les pré-requis de la partie 1 il y a: “comprendre parfaitement les fonctions (et donc les paramètres avancées en Python);”.

    Mouahahaha !

  • Feadurn

    Petite question lors du premier code montrant la POO:

    def get_url(self):
            return QUESTION_URL.format(title=self.title, id=self.id)

    le title=self.title sert à quelque chose ici ?

  • Tiger-222

    Génial la série !
    Petites coquilles, si elles en sont :

    Dans le paragraphe Petit détour par les attributs de classe, il y a un morceau de code où il faudrait modifer ‘…’ par ‘>>>’:
    ... UnObjetQuiPassaitParLa.methode_de_classe()
    >>> UnObjetQuiPassaitParLa.methode_de_classe()

    Et il y a un i qui traine en bonus :
    Avec une prioperty

    :)

  • Réchèr

    Allez hop, deux dernières petites corrections :

    Comme je vous le disais précément
    Comme je vous le disais précédemment

    On en va bien entendu pas s’arrêter là,
    On ne va bien entendu pas s’arrêter là,

    La classmethod qui génère des objets, je trouve ça assez inhabituel. Le code qui crée les objets, je le met rarement dans l’objet lui-même (sauf pour des trucs vraiment spécifiques genre un singleton). Mais chacun son style.

  • Sam Post author

    Merci Réchèr. Pour ce qui est de la classmethod, c’est pourtant une technique assez courante appelée le pattern “factory”.

  • Réchèr

    De rien, monsieur le canidé.

    “factory”. Oui c’est un mot que j’ai entendu ici et là. Je savais pas que ça correspondait à un design pattern particulier. Pour moi, c’était un bout de code externe qui générait des instances de classe. Mais j’avais pas réalisé que le bout de code pouvait être “interne”.

  • NicoS

    Bonjour,

    j’ai tenté de faire tourner le code sur ma machine mais le CSV que je reçois ne contient que l’ID du post dans la deuxième colonne, et non pas le JSON, alors que quand je copie-colle l’URL dans chrome, le CSV téléchargé contient bien le JSON dans la deuxième colonne…
    Bizarre…

  • sensini42

    Je poursuis ma lecture instructive et note mes corrections :
    Toujours les ; à la fin des items énum (. à la fin du dernier)
    Toujours les espaces avant :;!?
    lien !wfr Json : JSON

    »» print article.titre
    Votre Python aime les pip’ : pas de ‘

    Si j’affiche le titre et les id de l’article 1, ça donne ça : titre/id/article1 (sans espace) en balise code
    regardez le print_self : print_self en balise
    Pareil pour l’article2 : article2 en balise code
    c’est à dire : c’est-à-dire
    chanque entrée : chaque entrée

    # au crée un objet question dans lequel : on crée ? objet alors que dessous c’est la classe -> prête à confusion
    #fameux “object en cours” : objet
    #…des objets Question : des objets instance de Question (idem prête à confusion)
    A première vue : À
    Ca : Ça
    Au lieu d’avoir un dictionnaire …, on a un objet question : balise sur Question

    L’interêt : L’intérêt
    #fameux “object en cours” : objet

    mignone : mignonne
    quelque chose liée : lié

    (on passe de ça) A : À

    cette exemple simple : cet

    juste a apdater : juste à adapter (combooo)

    (Sur ce, je vais manger…)

  • aiki

    Juste une faute de frappe à changer

    Astuce au passage : si vous ouvrez le CSV issu de data.stackexchange.com, vous noterez qu’il y a plein de petits détails qui rendent ce format pas toujours facile à parser. Plutôt que d’y aller comme un bourrin avec des split(), nous utilisons donc le module csv pour récupérer chanque entrée du fichier comme un dictionnaire qui aura la structure:

    chanque = chaque

  • jojo

    Coucou sametmax et merci pour votre site tres sympa.

    J’ai repéré :

    une chose non précisée qui peut être préjudiciable aux débutants dans cet article.

    attention, les attributs de classe et les atributs de classe accédés depuis les instances ne recouvrent pas la même chose. En effet il est possible de modifier un attribut de classe au niveau de l’instance, ce qui ne modifie pas l’attribut de classe en lui-même. Pour être sûr d’accéder à la variable de classe il faut utiliser MaClasse.mon_attribut_de_classe

    un truc à updater dans la partie 1 du tuto : la classe construite l’est “à l’ancienne” car depuis python 2.x il vaut mieux utiliser les nouvelles classes

    class TrucBidule(object) qui derivent toutes de la classe object

  • Sam Post author

    Salut jojo, les deux points que tu soulignes sont volontairement expliqués dans les parties suivantes. Il y a 8 parties.

  • Olivier

    Super site, bravo à l’équipe.

    Sujet court et dense mais lisible.

    A la fin il y une typo:

    print “{creation_date} : {url}”.format(creation_date=question.creation_date,

    url=question.url)

    C’est plutot (enfin je crois)

    print “{creation_date} : {url}”.format(creation_date=question.created,

    url=question.url)

  • maykiwo

    Bonjour,

    Merci pour les explications, c’est super clair.

    Juste une coquille dans le texte

    print "{creationData} : {Post Link}".format(**question)

    à remplacer par

    print "{CreationDate} : {Post Link}".format(**question)

  • daimebag

    Merci pour les articles sur la POO ils sont top, ça complète bien le bouquin que je suis en train de lire!

    Sinon comme j’utilise python3 je fais(j’sais pas si c’est ce qu’il y a de mieux j’y connais que dalle):

    import request

    […]

    csv_data = StringIO(requests.get(URL).text)

    au lieu de:

    import csv

    […]

    csv_data = StringIO(urllib2.urlopen(URL).read(100000).decode(‘utf8’))

    ‘fin je dis ça parce que j’connais pas trop la lib request, mais si j’ai bien compris elle s’occupe de tout pour ce qui est de l’encodage c’est ça?

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.