Prérequis pour cette partie :
- avoir lu la partie 1.
- comprendre yield.
- comprendre les décorateurs.
- comprendre les bases du format CSV et JSON.
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 "{creationData} : {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.
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
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 !
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 !
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
Comment mon commentaire (qui était une réponse à Kontre) s’est retrouvé en haut ?
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
Hé ban, tu t’en pose des questions,toi..
T’inquiète, on trie…
Y’a des paradoxes spatio-temporels sur ce site !
Du coup on dirait que foxmask répond à Sam ^^
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 ?
A rien, j’ai fumé la moquette. Corrigé :-)
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) !
;-)
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?
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.
Ok, clair. Merci.
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 :)
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.
me revoilou avec une remarque sur le bout de code
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 ;)
C’est vrai. Je pourrais le rendre explicite, ce serait plus pédagogique.
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 !
Petite question lors du premier code montrant la POO:
le
title=self.title
sert à quelque chose ici ?A rien du tout. C’est un pet de code.
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
:)
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.
Merci Réchèr. Pour ce qui est de la classmethod, c’est pourtant une technique assez courante appelée le pattern “factory”.
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”.
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…
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…)
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
Merci
Hello Sam,
j’ai posé une question en relation avec ce tuto ( génial) sur http://indexerror.net/2128/int-object-is-not-subscriptable?show=2129#a2129
si tu peux jeter un oeil ça serait cool :-)
Merci à toi
cdt
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
Salut jojo, les deux points que tu soulignes sont volontairement expliqués dans les parties suivantes. Il y a 8 parties.