PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Si vous ne savez pas ce que contient une variable, vous ne comprenez pas le programme

mardi 12 février 2013 à 14:41

L’immense majorité des questions qu’on me pose sur le fonctionnement d’un programme (ou sur des détails dans les tutos) peut être résolu d’une seule manière.

En sachant ce qu’il y a dans les variables.

Un ordinateur est une machine à états. Les variables représentent cet état. Le seul moyen de comprendre ce que fait un programme, c’est de savoir à quel moment il est dans quel état.

Quand vous ne comprenez pas ce que fait un programme, la première et plus importante question à vous poser et donc :

Que contient cette variable ?

Répondre à la question

La beauté de Python, c’est qu’il vous permet de très facilement répondre à cette question par vous même. Tout ce que vous avez à faire c’est copier le code, le mettre dans un fichier (ou un shell), et le lancer.

Ne restez pas passif devant un tuto. Vous n’en comprendrez que la moitié (au mieux).

Un tuto, une doc, un cours, un snippet ne se lit pas, il se travaille.

Voici tout ce que vous pouvez faire à une variable pour obtenir des informations sur elle :

>>> i = 1
>>> print(i) # afficher la variable
1
>>> print(type(i)) # afficher son type
<type 'int'>
>>> print(i.__class__) # afficher la classe dont elle est issue
<type 'int'>
>>> print(i.__class__.__name__) # affiche le nom de sa classe
>>> print(dir(i)) # afficher les méthodes de cet objet
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']
>>> help(i) # aide sur cet objet
Help on int object:
 
class int(object)
 |  int(x[, base]) -> integer
 |  
 |  Convert a string or number to an integer, if possible.  A floating point
 |  argument will be truncated towards zero (this does not include a string
 |  representation of a floating point number!)  When converting a string, use
 |  the optional base.  It is an error to supply a base when converting a
 |  non-string.  If base is zero, the proper base is guessed based on the
 |  string content.  If the argument is outside the integer range a
 |  long object will be returned instead.
 |  
 |  Methods defined here:
 |  
 |  __abs__(...)
 |      x.__abs__() <==> abs(x)
...

Ces informations répondent à la question “qu’est-ce que c’est”, “comment c’est configuré” et “qu’est-ce que ça peut faire”.

C’est très important. Vous ne pouvez pas comprendre ce que fait un programme sans ces informations.

En Python une variable peut contenir n’importe quoi

J’ai dis n’importe quoi.

Pas juste les types de base :

>>> i = 'a'
>>> type(i)
<type 'str'>
>>> i = {}
>>> type(i)
<type 'dict'>
>>> i = []
>>> type(i)
<type 'list'>

Ni même juste des résultats de fonctions :

>>> i = test()
>>> type(i)
<type 'int'>

Non. Les variables Python peuvent contenir les fonctions elles-même !

>>> type(test)
<type 'function'>
>>> i = test
>>> i()
1
>>> type(i)
<type 'function'>

Et là ça devient important de savoir de quoi on parle :

>>> i.__name__
'test'

Mais les variables peuvent aussi contenir des instances de vos classes :

>>> class Essai(object):
...     pass
... 
>>> i = Essai()
>>> type(i)
<class '__main__.Essai'>

Sauf que les variables peuvent aussi contenir les classes elles-même !

>>> type(Essai) # les classes sont de type 'type'. Funky !
<type 'type'>
>>> i = Essai # ceci n'est PAS une instance. Ce n'est PAS Essai().
>>> type(Essai)
<type 'type'>
>>> i.__class__
<type 'type'>
>>> i.__name__
'Essai'

Les fonctions et les classes ne sont pas juste des noms en Python. Ce sont des choses que vous pouvez manipuler. Donc on les retrouve dans des variables.

Donc vous n’avez pas le choix. Vous devez savoir de quoi on parle. Et il faut donc bien comprendre ce qu’est un type, et qu’est-ce qui est de quel type :

>>> type(str) # str est une classe
<type 'type'>
>>> type(str()) # le résultat de l'appel de str est une string
<type 'str'>
>>> type(Essai) # Essai est une classe
<type 'type'>
>>> type(Essai()) # le résultat de l'appel de Essai est un objet Essai
<class '__main__.Essai'>
>>> type('1') # '1' est une string
<type 'str'>
>>> type(1) # 1 est un int
<type 'int'>
>>> type(True)
<type 'bool'> # True est un boolean

Si vous ne comprenez pas les types, vous ne pouvez PAS comprendre un programme.

Ce n’est pas honteux. Arrêtez d’essayer un truc impossible, c’est tout. Revenez en arrière. Allez apprendre ce qu’est un type x ou z.

Puis retournez essayer de comprendre le programme.

Il n’y a PAS d’alternative.

Quand vous lisez un programme, vous devez savoir ce qu’il y a dans une variable :

Pdb à la rescousse

Donc quand vous lisez un tuto, vous allez copier / coller du code. Vous allez le lancer. Si vous ne le faites pas, vous ne comprendrez pas.

Maintenant, dans un premier temps vous allez utiliser des print.

print type(truc)
print truc.__class__
print dir(truc)

Et pour les fonctions et les classes :

print truc.__name__

Mais on peut faire mieux. Utilisez le debugger de Python intégré : PDB. J’ai écrit un bon article sur la question, mais pour résumer, quand vous voulez avoir des informations sur une variable dans un programme, juste avant la variable faites :

import ipdb; ipdb.set_trace()

Puis lancez le programme. Un shell va s’ouvrir, bloquant le programme à cet endroit. Et vous pourrez manipuler la variable à ce moment du programme.

Pour quitter, entrez q.

flattr this!

Désolé pour le merdouillage dans les URLs

lundi 11 février 2013 à 18:43

Je sais qu’il y a des gens qui suivent le blog via les emails, et dès que je sauvegarde un mail part avec un lien. Mais parfois je change le lien en route car j’ai foiré l’URL (genre j’ai mis partie 3 sur le dernier article alors que c’était la partie 5).

Sorry.

flattr this!

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 5.

lundi 11 février 2013 à 18:38

Maestro, musique !

Prérequis :

La POO, c’est comme les poupées russes. Une fois qu’on a maîtrisé un concept, paf, y en a un autre qui se ramène derrière.

J’ai plusieurs mamans

En ces temps de polémique sur l’autorisation du mariage entre êtres amoureux et consentants (par opposition à celui qu’on fait par convention sociale depuis des siècles), je vous propose de vous rappeler qu’une fois de plus les informaticiens sont en avance sur les mœurs.

D’abord parce qu’une communauté qui fait autant de links vers Never Gona Give You Up est forcément pro gay par essence. Ensuite parce qu’une classe fille peut avoir plusieurs classes mères sans que ça choque personne.

Prenez ces deux classes qui passaient par là sans rien demander :

class MouMoutte(object):
 
    type = 'top' # c'est tip top moumoutte
 
class Raoul(object):
 
    trop = 'cool'

Hé merde, j’ai écris ça, et maintenant j’ai aucune idée de comment faire un code cohérent à partir de cet exemple à la con.

On annule tout.

On recommence.

Vous faites un jeu vidéo. Ça, ça parle bien. Et dedans vous avez des protections et des armes.

class Arme(object):
 
    def __init__(self, nom, degat):
 
        self.nom = nom
        self.degat = degat
 
    def attaque(self, cible): # on retire les degâts de l'épee des points de vie
        cible.vie -= self.degat
 
 
class Protection(object):
 
    def __init__(self, nom, armure):
 
        self.nom = nom
        self.armure = armure
 
    def defend(self, degat): # on diminue les degâts, voire on les annule
 
        degat = degat - self.armure
        if degat < 0:
            return 0
 
        return degat
 
>>> epee = Arme('Epée Mana', degat=999)
>>> casque = Protection('Casque de Balduran', armure=1)

C’est simpliste, mais vous voyez le tableau. Maintenant un connard de client arrive et vous sort une idée trop cool : il faudrait ajouter un barbare dans le jeu. Qui tape aussi avec son bouclier, parce que la concurrence le fait et qu’ils veulent pas se faire mettre un vent par Blizzard.

Enfer et Rutabaga ! Comment allons nous nous sortir de cette situation ?

Il y a moult manières de faire, mais l’une d’elle est d’utiliser l’héritage multiple, c’est-à-dire de créer une classe qui hérite des deux classes en même temps.

class ProtectionOffensive(Arme, Protection):
 
    def __init__(self, nom, degat, armure):
 
        Arme.__init__(self, nom, degat) # appelle le __init__ de arme
        Protection.__init__(self, nom, armure) # appelle le __init de protection
 
        # comme on a appelé les deux __init__, on va avoir les attributs
        # settés dans les deux __init__ attachés à cette classe

Nous avons alors une classe qui possède les méthodes des deux classes parentes :

>>> bouclier = ProtectionOffensive('Bouclier du dragon', degat=10, armure=100)
>>> bouclier.degat
10
>>> bouclier.armure
100
>>> bouclier.defend(10)
0

Ne cherchez pas compliqué, ça fait exactement ce que ça à l’air de faire : “copier” (oui bon, entre guillemets) le code de chaque parent dans l’enfant.

Néanmoins vous avez vu qu’il y a quelques subtilités, notamment la partie __init__.

Posez-vous deux minutes. Respirez. Concentrez-vous. Prêt ?

Les deux classes parentes ont une méthode __init__, mais Python ne peut en “copier” qu’une seule dans l’enfant. Il copie donc la première qu’il trouve. Il va prendre la liste des parents (ici: Arme, Protection), et la lire de gauche à droite. Il va regarder chaque parent, et si la méthode existe, il va la “copier” dans l’enfant.

Si il retrouve une méthode de même nom dans un des parents suivants, il l’ignore. (Je dis un DES parents suivants car vous pouvez avoir 10 parents si vous voulez).

Donc dans notre exemple, si je fais :

class ProtectionOffensive(Arme, Protection):
    pass

ProtectionOffensive n’aura que la méthode __init__ de Arme. Or ce n’est pas ce qu’on veut. On va donc overrider la méthode __init__, et dedans appeler la méthode __init__ de Arme ET celle de Protection.

Cette syntaxe : Classe.methode(self, args...) que l’on retrouve dans Arme.__init__(self, nom, degat) est juste un moyen d’appeler spécifiquement la méthode du parent.

Dans la partie précédente, je vous ai montré qu’on pouvait faire cela avec super(). Or super() vous retournera la première méthode du premier parent qu’elle trouve : c’est le but de super(), de faire ça automatiquement sans se soucier de savoir qui est le premier parent à avoir une méthode du bon nom.

C’est utile car parfois c’est le parent du parent du parent qui a la méthode qu’on veut appeler. On ne connaît pas forcément son nom, ou alors on ne veut pas l’écrire en dur. Mais dans notre cas, on veut spécifiquement une méthode d’un parent en particulier, il faut donc l’écrire à la main.

D’une manière générale :

Faites attention !

Le self n’est pas au même endroit dans super(ClassCourante, self).methode(args...) et ClasseParente.methode(self, args...). Et dans le premier cas, on passe la classe courante (que super() va l’analyser pour trouver les parents automatiquement), dans le cas suivant, on écrit le nom de la classe parente en dur.

Faites quelques tests avec des scripts bidons pour bien comprendre comment ça marche. Faites ça avec des classes toutes simples. Sinon le jour où vous aurez une classe compliquée, vous allez vous embrouiller.

La composition (POO pour les vrais mecs)

Jusqu’ici c’était un tuto avec des notions de base pour des petits geeks imberbes qui jouent avec des actions figures fabriquées en Chine et achetées sur ebay. Mais maintenant nous allons voir la POO pour les vrais hommes, les barbus, ceux qui jouent avec des reals dolls et qui n’ont pas peur de mettre des chaussettes dépareillées.

Voyez-vous, un objet, tout seul, il sert à rien. Il s’emmerde déjà, rien à foutre le samedi, nul part où sortir, tout ça. Mais surtout, il a personne pour prendre l’apéro. Non, dans un programme digne d’un vrai pastis, il faut plusieurs objets qui interagissent entre eux. En fait, plusieurs objets qui s’utilisent les uns les autres.

Retournez à notre exemple de jeu video :

class HeroViril(object):
 
    # def __init__(self, nom, prenom, groupe_sanguin, signe_astrologique,
    #              couleur_preferee, tendance_sexuelle, culte,
    #              taille_de_la_troisieme_phallange_de_l_index_gauche)
    # TODO : voir le CDC avec le client pour confirmer les attributs du personnage
    def __init__(self, nom, vie, arme=None):
 
        self.nom = nom
        self.vie = vie
        self.arme = arme
 
    def combattre(self, ennemi):
 
        print "{} attaque {}".format(self.nom, ennemi.nom)
 
        while True:
 
            if self.arme:
                self.arme.attaque(ennemi)
 
            if ennemi.vie <= 0:
                break
 
            if ennemi.arme:
                ennemi.arme.attaque(self)
 
            if self.vie <= 0:
                break
 
        if self.vie > 0:
            print "Victoire de {}".format(self.nom)
        else:
            print "{} est mort comme une pov' merde".format(self.nom)

Et là vous notez un truc, c’est que nous n’avons pas de méthode attaque() sur notre héros. Nous utilisons la méthode attaque d’un objet arme. Que l’on a en attribut.

C’est cela la composition : un objet, qui en fait est composé de plusieurs sous-objets. Dans notre cas, notre objet héros est aussi composé d’une arme et d’une protection, qui sont ses attributs. Il peut ainsi utiliser le comportement de ses objets pour faire le boulot à sa place : c’est ce qu’on appelle la délégation.

Reprenons notre code des armes, un peu adapté :

# le code de l'armure ne change pas
class Protection(object):
 
    def __init__(self, nom, armure):
 
        self.nom = nom
        self.armure = armure
 
    def defend(self, degat):
 
        degat = degat - self.armure
        if degat < 0:
            return 0
 
        return degat
 
 
# on change le code de l'arme, si la cible a une protection
# cela diminue les degâts pris
class Arme(object):
 
    def __init__(self, nom, degat):
 
        self.nom = nom
        self.degat = degat
 
    def attaque(self, cible):
 
        # je mets aussi quelques prints pour le lulz
        if cible.protection:
            degat = cible.protection.defend(self.degat)
            print "{} - {} = {}".format(cible.vie, degat, cible.vie - degat)
            cible.vie -= degat
        else:
            print "{} - {} = {}".format(cible.vie, self.degat, cible.vie - self.degat)
            cible.vie -= self.degat

Maintenant créons deux héros, armons-les, et faisons-les combattre :

>>> gosu = HeroViril("Drizzt Do'Urden", 2000)
>>> gosu.arme = Arme('Lame Vorpale', 10)
>>> gosu.protection = Protection("Maille en Kevlar de mithril doré a l'adamantium", 10)
>>> noob_qui_repop = HeroViril("Bob", 200)
>>> noob_qui_repop.arme = Arme('Cure-dent', 1)
>>> noob_qui_repop.protection = Protection("Slip", 1)
>>> noob_qui_repop.combattre(gosu) # yaaaaaaaaaaaaaaaaaaaaaaa !
 
Bob attaque Drizzt Do'Urden
2000 - 0 = 2000
200 - 9 = 191
2000 - 0 = 2000
191 - 9 = 182
2000 - 0 = 2000
182 - 9 = 173
2000 - 0 = 2000
173 - 9 = 164
2000 - 0 = 2000
164 - 9 = 155
2000 - 0 = 2000
155 - 9 = 146
2000 - 0 = 2000
146 - 9 = 137
2000 - 0 = 2000
137 - 9 = 128
2000 - 0 = 2000
128 - 9 = 119
2000 - 0 = 2000
119 - 9 = 110
2000 - 0 = 2000
110 - 9 = 101
2000 - 0 = 2000
101 - 9 = 92
2000 - 0 = 2000
92 - 9 = 83
2000 - 0 = 2000
83 - 9 = 74
2000 - 0 = 2000
74 - 9 = 65
2000 - 0 = 2000
65 - 9 = 56
2000 - 0 = 2000
56 - 9 = 47
2000 - 0 = 2000
47 - 9 = 38
2000 - 0 = 2000
38 - 9 = 29
2000 - 0 = 2000
29 - 9 = 20
2000 - 0 = 2000
20 - 9 = 11
2000 - 0 = 2000
11 - 9 = 2
2000 - 0 = 2000
2 - 9 = -7
Bob est mort comme une pov' merde

Ok, j’ai pigé le principe, mais comment ça marche dans le détail ?

Regardons la méthode combattre de plus près :

    # elle attend un ennemi en paramètre, donc UN OBJET HeroViril
    # self est l'objet en cours, donc aussi un objet HeroViril
    def combattre(self, ennemi):
 
        print "{} attaque {}".format(self.nom, ennemi.nom)
 
        # une petite boucle infinie. Warning, c'est un tuto. Ne faites pas
        # ça chez vous les enfants.
        # cette boucle loop pour toujours si il n'y a pas d'attribut arme donc
        # ceci n'est qu'un exemple. Hein ? Noté ? Les deux du fond là ?
        while True:
 
            # on donne le premier coup à la personne qui attaque (l'objet en
            # cours). On vérifie qu'il a une arme. Si c'est le cas,
            # on appelle la méthode de l'arme "attaque()", et on lui passe
            # en paramètre l'ennemi.
            if self.arme:
                self.arme.attaque(ennemi)
 
            # condition de sortie de la boucle sur la vie du héros qui a pris
            # le coup
            if ennemi.vie <= 0:
                break
 
            # ensuite on fait pareil à l'envers pour donner une chance à l'autre
            # de répliquer : on vérifie que l'ennemi a une arme, et si c'est
            # le cas, on applique la méthode "attaque" de l'arme à l'objet
            # en cours
            if ennemi.arme:
                ennemi.arme.attaque(self)
 
            # condition de sortie de la boucle sur la vie du héros qui a pris
            # le coup
            if self.vie <= 0:
                break
 
        # une fois sorti de la boucle, on vérifie le niveau de vie pour
        # désigner le vainqueur
        if self.vie > 0:
            print "Victoire de {}".format(self.nom)
        else:
            print "{} est mort comme une pov' merde".format(self.nom)

Donc combattre utilise un objet arme, et appelle sa méthode attaque() sur un héros (j’ai viré les prints pour rendre le truc plus clair) :

    # self est l'objet en cours, donc l'arme
    # cible est un héros, puisqu'on l'a passé en paramètre
    def attaque(self, cible):
 
        # si la cible (l'objet héros) a un attribut protection,
        # les dégâts retirés sont diminués (ce calcul est fait par la protection)
        if cible.protection:
            cible.vie -= cible.protection.defend(self.degat)
 
        # sinon, on retire les dégâts à la vie de la cible (le héros)
        # directement
        else:
            cible.vie -= self.degat

Diantre ! La méthode attaque utilise elle-même la méthode defend() de la protection :

    # self est l'objet en cours, donc la protection
    # degat est un simple int
    def defend(self, degat):
        # on retourne les degâts infligés, moins la protection
        return degat - self.armure

Pour comprendre tous ces codes, il faut bien piger deux trucs :

Ce dernier point est le plus important. Si vous comprenez ça, vous avez maîtrisé le plus important de la POO. Relisez le plusieurs fois :

Les héros ont une référence aux armes. Et ensuite, on passe une référence des héros aux armes. Les armes retirent de la vie à ces héros, non sans calculer les dégâts en fonction de la protection qu’ils portent.

Les objets ont tous des références les uns vers les autres. Ils se manipulent tous les uns les autres.

Cela fait bizarre car dans la vie une épée ne manipule pas un héros (bon, je connais peu d’elfes noirs IRL aussi). On comprend facilement qu’un héros ait un attribut ‘épée’, mais il est difficile de comprendre qu’une épée ait une méthode, et que le paramètre de cette méthode soit un héros.

C’est un concept purement informatique : la logique des dégâts est codée dans l’arme, pas dans le héros. L’avantage de cette architecture, c’est que si vous changez l’arme, vous changez toute la logique des dégâts. Par exemple, vous rajoutez une arme empoisonnée :

class ArmeMegaEmpoisonnee(Arme):
 
    def __init__(self, nom, degat, poison=100000):
        super(ArmeMegaEmpoisonnee, self).__init__(nom, degat)
        self.poison = poison
 
 
    def attaque(self, cible):
 
        # on prend les degâts une premiere fois
        super(ArmeMegaEmpoisonnee, self).attaque(cible)
 
        # puis on prend les dégâts du poison
        cible.vie -= self.poison

Cette arme fait plus de dégâts. Son mécanisme pour faire des dégâts est différent. Il suffit d’équiper un héros avec l’arme (en changeant l’attribut) pour que ce nouveau calcul de dégâts soit pris en compte.

>>> noob_qui_repop.vie = 200 # rePOP !
>>> noob_qui_repop.arme = ArmeMegaEmpoisonnee('Cheat Code', 1)
>>> noob_qui_repop.combattre(gosu) # Vengeance !
Bob attaque Drizzt Do'Urden
2000 - 0 = 2000
Victoire de Bob

Ce qu’il faut retenir : on peut mettre des objets en tant qu’attributs d’objets. Il n’y a pas de limite dans les nombres d’objets à mettre, leur mélange, les niveaux d’imbrications, etc. On peut mettre des objets, dans des objets, dans des objets… C’est la composition.

Et les objets peuvent utiliser les méthodes des autres objets. Et on peut passer des objets comme paramètres à des méthodes. C’est la délégation.

C’est la partouze des objets, quoi.

On peut mettre des objets dans des sets, des dicos, des listes… Par juste des attributs. Il y en a des choses à faire !

Choisir entre l’héritage et la composition

Les deux techniques permettent de réutiliser du code, mais pas de la même façon. Aucune règle générale ne tient la route dans tous les cas, mais un bon point de départ est de se dire que :

Il y a aussi, quelque part, dans le lointain pays des enculeurs de mouche, une différence entre l’agrégation (qu’on a pas vu) et la composition. Vous vivrez très bien en considérant que c’est la même chose.

Le design pattern “stratégie”

Vous entendrez parfois parler du motif de conception “stratégie”. C’est en fait une mise en application abstraite de la composition.

Normalement la composition s’utilise avec des “part de” concrètes. Vous avez une voiture : elle est composée d’objets pneus, d’un objet moteur, etc.

Le design pattern stratégie est l’extraction d’une part du comportement d’un objet pour le mettre dans un autre objet, mais la nature de l’objet importe peu. Ceci est fait purement pour découpler le comportement de l’objet.

On a vu plus haut que changer l’arme permet de changer le calcul des dégâts. C’est ce type de résultat qu’on vise avec le design pattern strategy.

import os
 
class ParseurXml(object):
    ...
 
class ParseurJson(object):
    ...
 
 
class ParseurDeFichier(object):
 
    _strategy = { # les stratégie par défaut
        'json': ParseurXml,
        'xml': ParseurJson
    }
 
    def __init__(self, fichier, strategy=None):
 
        self.fichier = fichier
        # on récupère l'extension du fichier
        path, ext = os.path.splitext(fichier)
 
        # Strategy est une classe de parseur
        # on la récupère depuis les paramètres ou selon
        # l'extension
        Strategy = strategy or self._strategy[ext.lstrip('.')]
        # on instancie notre classe de strategie
        self.strategy = Strategy(fichier)
 
    def parse(self):
        # on délègue le boulot à la stratégie
        self.strategy.parse()

La ligne la plus importante est :

Strategy = strategy or self._strategy[ext]

Ici on dit récupérer la stratégie de parsing en paramètre, ou sinon, la bonne en fonction de l’extension de fichier. On charge donc une classe dynamiquement, on va créer un objet à partir de cette classe. Et c’est cet objet à qui on va déléguer le comportement du parseur :

    def parse(self):
        self.strategy.parse()

On utilise l’objet dynamiquement pour gérer tout le parsing. On peut ainsi choisir un parseur à la volée.

La pattern strategy mélange donc composition (l’objet strategy est une part de l’objet général), délégation (l’objet général utilise le comportement de l’objet strategy) et d’injection de dépendance (on peut changer l’algo à la volée, il suffit de changer de stratégie).

Bon, ça c’était le mode hard. C’est pas grave si vous finissez pas le niveau toute de suite. Mettez pause. Allez pisser un coup et revenez- plus tard, les continues sont infinis sur le blog.

flattr this!

Ouvrir un port sur un serveur avec Iptables

dimanche 10 février 2013 à 07:50

Dans la série “les commandes dont je me souviens jamais”, voici la commande qui permet d’ouvrir un port sur le firewall iptable de son serveur (il faut les droits admin) :

iptables -A INPUT -p tcp -m tcp --dport 7777 -j ACCEPT # ouvrir le port 777
iptables -D INPUT -p tcp -m tcp --dport 7777 -j ACCEPT # annuler l'ouverture
iptables -L # regarder ce qui est ouvert et ce qui l'est pas

Et après y a des gens qui trouvent que git est cryptique.

Très utile pour la session panique de debuggage en prod sur ce fameux bug aléatoire qui n’arrive pas sur les machines de dev ni la machine d’intégration (quand il y en a une…).

En effet, avec Python et WSGI, tant qu’on relance pas le process, les changements de codes sont pas pris en compte. Donc je met DEBUG = True, je lance ./manage.py runserver_plus 0.0.0.0:7777, et je debug la prod sans que l’instance qui sert les clients soient affectées par mes conneries.

flattr this!

Le vendredi, c’est entre ami

samedi 9 février 2013 à 05:49

Qu’est-ce qui est meilleurs à plusieurs, est facilité par l’alcool et généralement pratiqué par des gens qui ont une vision tordue des relations humaines ? Le sprint bien entendu.

Vendredi, ce n’est pas que pour les trolls. C’est aussi l’occaz’ de relancer le script “./biere_pizza.sh --potes --projet“.

Comme on a des vieux dans l’équipe, ils ont fait l’impasse sur la nuit blanche et je poste cet article à seulement 5h du mat. On a même pas un proto qui marche. La honte.

Mais un sprint c’est aussi (et surtout), le moment de se marrer un bon coup, et pour ça par contre on est super au point.

Donc, résumé de la soirée, en une photo d’un des postes de “travail”:

Nerf, rubiscube, prothèses mammaire, aspirine et coca-cola

Les prothèses mammaires font d'excellents anti-stress

Si on finit le proto, promis on vous donne l’accès en premier (avec un bel article qui présente le concept sous forme de slides corporates incluant des pie charts et des mots comme “synergie sociale” et “proactivité génératrice de buzz”). Sinon, mieux vaut pas préciser le sujet de notre échec cuisant :-)

Vous l’aurez compris, ce post est surtout là pour se la péter avec notre vie trop cool, tiser un peu sur un projet qui ne sortira peut être jamais et servir d’excuse pour poster une photo marrante.

C’est le WE quoi.

flattr this!