error – Sam & Max http://sametmax.com Du code, du cul Wed, 30 Oct 2019 15:34:04 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Set -e for the win http://sametmax.com/set-e-for-the-win/ http://sametmax.com/set-e-for-the-win/#comments Thu, 14 Mar 2013 08:26:52 +0000 http://sametmax.com/?p=5408 10 ans plus tard, je découvre cette option pour les scripts bash.

Voyez vous, quand on écrit un script bash, il exécute les commandes ligne à ligne. Si il y en a une qui retourne une erreur, il continue vaillamment.

Sur un script où chaque ligne dépend de la précédente (comme un script de déploiement), c’est moyen.

Pour y pallier, il suffit de mettre tout en haut du script :

set -e 

Et voilà. Si une commande retourne une erreur, le script s’arrête à ce niveau. 10 ans de Linux et je découvre ça maintenant. Moi content.

]]>
http://sametmax.com/set-e-for-the-win/feed/ 7 5408
nginx: [emerg] “worker_processes” directive is not allowed here in /usr/local/nginx/conf/nginx.conf:3 http://sametmax.com/nginx-emerg-worker_processes-directive-is-not-allowed-here-in-usrlocalnginxconfnginx-conf3/ http://sametmax.com/nginx-emerg-worker_processes-directive-is-not-allowed-here-in-usrlocalnginxconfnginx-conf3/#comments Wed, 13 Feb 2013 10:05:56 +0000 http://sametmax.com/?p=4452 include *.conf dans son nginx.conf et que cela inclu nginx.conf lui même. ]]> Cette erreur arrive quand la directive “worker_processes” est incluse une deuxième fois. Typiquement cela arrive quand on a include *.conf dans son nginx.conf et que cela inclut nginx.conf lui même.

Il faut donc être spécifique sur les fichiers à inclure include site.conf ou déplacer nginx.conf dans un autre dossier que celui des sous fichiers de configuration.

]]>
http://sametmax.com/nginx-emerg-worker_processes-directive-is-not-allowed-here-in-usrlocalnginxconfnginx-conf3/feed/ 2 4452
TypeError: Error when calling the metaclass bases function() argument 1 must be code, not str http://sametmax.com/typeerror-error-when-calling-the-metaclass-bases-function-argument-1-must-be-code-not-str/ http://sametmax.com/typeerror-error-when-calling-the-metaclass-bases-function-argument-1-must-be-code-not-str/#comments Wed, 16 Jan 2013 12:16:23 +0000 http://sametmax.com/?p=4150 Cette erreur est souvent déclenchée quand on essaye d’hériter d’une fonction au lieu d’une classe. Cela peut arriver par erreur avec des fonctions qui sont nommées en CamelCase, en dépit du PEP8.

Par exemple:

class Truc(threading.Condition):
    pass

class Machine(tempfile.NamedTemporaryFile):
    pass

Lèveront l’exception :

TypeError: Error when calling the metaclass bases 
    function() argument 1 must be code, not str

Car:

>>> import threading, tempfile
>>> type(threading.Condition)

>>> type(tempfile.NamedTemporaryFile)

Malgré leurs noms en majuscule.

Le message d’erreur est lui-même complètement obscure. Bref, le genre de truc qui est 100% lié à des erreurs d’autres personnes que vous aller payer par une après-midi de debug si on ne vous donne pas la solution.

Mais bon, la queue de celui qui n’a jamais pourri l’après-midi d’un autre codeur avec son travail merdique jette le premier parpaing.

]]>
http://sametmax.com/typeerror-error-when-calling-the-metaclass-bases-function-argument-1-must-be-code-not-str/feed/ 7 4150
L’indentation de Python m’a tuer http://sametmax.com/lindentation-de-python-ma-tuer/ http://sametmax.com/lindentation-de-python-ma-tuer/#comments Thu, 09 Aug 2012 13:01:31 +0000 http://sametmax.com/?p=1568 J’adore le fait que Python se base sur des espaces pour délimiter les blocs de code. La seule contrainte que cela a jusqu’ici posé, c’est que les blocs ne peuvent pas être passés en arguments.

Aujourd’hui pourtant, ce système si merveilleux nous a bien fait chier pendant une demi-heure.

Cas simple: Max me file un snippet bien racheux dans 0bin. Je clic sur “copy to clipboard”, j’élague la fonction des loggers et des try/catch qui attrapent tout, même un rhûme, et je lance des tests.

  File "truc.py", line 35
     
    ^
SyntaxError: invalid syntax

Quid ?

Je cherche, je recherche, je creuse, je retourne, je m’enfonce.

Passage en mode fourmis.

Je retire des blocs. Des lignes une a une. Des combinaisons des deux. Des combinaisons arbitraires, aléatoires de blocs transposés dans un autre fichier après conversion en utf-8 et des tabs en espaces.

  File "truc.py", line racine de 12
     
    ^
SyntaxError: invalid syntax

Je prends Max a témoin.

Nous cherchons. Nous recherchons. Nous creusons. Je m’énerve.

Ce putain de code DOIT marcher. Il n’y a AUCUNE putain d’erreur de syntax là dedans.

Puis eureka, je copie et je colle le texte comme string dans le shell Python.

Lumière:

>>>"""def download(url, dest_path, progress_callback=lambda x, y: 0, proxy=None, block_sz=8192):
...         
...          
...             if proxy is not None:
...                         # build a new opener that uses a proxy requiring authorization
...                         proxy_support = urllib2.ProxyHandler({"http" : proxy})
...                         opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
...                  
...                         # install it
...                         urllib2.install_opener(opener)
...                  
...                     u = urllib2.urlopen(url, timeout=30)
...                     f = open(dest_path, 'w')
...                  
...                     meta = u.info()
...                 
...                     file_size = int(meta.getheaders("Content-Length")[0])
...                  
...                     block_sz = file_size_dl = 8192
...                     buffer  = u.read(block_sz)
...                     previous_status = ()
...                  
...                     while buffer:
...                          
...                                 file_size_dl += block_sz
...                                 f.write(buffer)
...                                 status = (file_size_dl, file_size_dl * 100. / file_size)
...                                 if status != previous_status:
...                                 """    
...            
             
'\ndef download(url, dest_path, progress_callback=lambda x, y: 0, proxy=None, block_sz=8192):\n    \n    \xc2\xa0\n        if proxy is not None:\n                # build a new opener that uses a proxy requiring authorization\n                proxy_support = urllib2.ProxyHandler({"http" : proxy})\n                opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)\n        \xc2\xa0\n                # install it\n                urllib2.install_opener(opener)\n        \xc2\xa0\n            u = urllib2.urlopen(url, timeout=30)\n            f = open(dest_path, \'w\')\n        \xc2\xa0\n            meta = u.info()\n        \n            file_size = int(meta.getheaders("Content-Length")[0])\n        \xc2\xa0\n            block_sz = file_size_dl = 8192\n            buffer  = u.read(block_sz)\n            previous_status = ()\n        \xc2\xa0\n            while buffer:\n            \xc2\xa0\n                    file_size_dl += block_sz\n                    f.write(buffer)\n                    status = (file_size_dl, file_size_dl * 100. / file_size)\n                    if status != previous_status:\n                            previous_status = status\n                            progress_callback(*status)\n                \n                        buffer = u.read(block_sz)\n                \xc2\xa0\n                \xc2\xa0\n                    f.close()\n                \xc2\xa0\n                '

Mais quel est ce petit salopard de “\xc2\xa0” ?

In [2]: print "a\xc2\xa0b"
a b


Caractère utf8 pour “espace insécable”
.

Pour Sublime Text et 0bin, se sont des espaces comme les autres. Pour Python, c’est une syntax error.

Fuck.

La coloration syntaxique de 0bin doit sans doute insérer ce truc à chaque saut de ligne. Du coup on a patché tout ça, les sources sont à jour sur github et j’en ai profité pour updater le paquet sur pypi qui est maintenant la dernière en date avec tous les goodies: détection de code source, send by mail, compteur, etc.

]]>
http://sametmax.com/lindentation-de-python-ma-tuer/feed/ 17 1568
Template de demande d’aide en informatique http://sametmax.com/template-de-demande-daide-en-informatique/ http://sametmax.com/template-de-demande-daide-en-informatique/#comments Sat, 21 Jul 2012 15:24:55 +0000 http://sametmax.com/?p=1237 Copiez/collez ce questionnaire, répondez, et postez le résultat sur le médium par lequel vous demandez de l’aide. Vous aurez une bien meilleure réponse:

========= Commencez la copie APRES cette ligne ================

Quel est mon objectif ? (Donnez un context général, mais précisez quel point en particulier vous pose problème)

Qu’est-ce que j’ai essayé de faire ? (Expliquez pas à pas. Postez aussi les bouts de code. Si c’est trop long, utilisez 0bin.net)

Quels résultats ai-je obtenus ? (Ne pas oublier les messages d’erreur).

Qu’est-ce que je pense que j’aurais dû obtenir au lieu de cela ? (Pour qu’on comprenne ce que vous comprenez)

Quelle est ma situation ? (Version, langue, marchine, système, etc. pour chaque logiciel et library)

========= Arrêtez la copie AVANT cette ligne ================

Inutile de préciser que c’est urgent ou que vous êtes un débutant. Écrire une bonne question prend du temps, comme écrire une bonne réponse. Vous êtes pressé ? Nous aussi. Il n’y a pas de raccourci.

Origine de cet article

J’aime beaucoup l’article “Comment poser une question intelligement” mais il fait 3 km de long et une personne qui ne prend pas le temps de formuler correctement sa demande a peu de chance de prendre le temps de le lire. L’approche de Google code est beaucoup plus efficace.

Bloggers et admins du monde (francophone) entier, ne vous embêtez plus à demander des précisions. Postez juste un lien vers cet article pour chaque personne qui ne donne pas toutes les infos.

]]>
http://sametmax.com/template-de-demande-daide-en-informatique/feed/ 5 1237
Quelques erreurs tordues et leurs solutions en Python http://sametmax.com/quelques-erreurs-tordues-et-leurs-solutions-en-python/ http://sametmax.com/quelques-erreurs-tordues-et-leurs-solutions-en-python/#comments Sun, 24 Jun 2012 02:29:56 +0000 http://sametmax.com/?p=995 Quand vous débuggez, rappelez-vous que pdb est votre ami, et qu’il est souvent bon de supprimer tous les fichiers .pyc pour éviter la confusion. Mais parfois l’erreur semble n’avoir aucun sens. Bien que Python soit un langage dont l’une des grandes qualités soit la cohérence, voici une liste d’erreurs et leurs solutions qui ont tendance à énerver (les erreurs hein, pas les solutions).

NameError: name 'x' is not defined

Python plante en annonçant que la variable n’est pas définie. Vous allez à la ligne donnée, et elle est là. Vous vérifiez qu’il n’y a pas de faute de frappe (genre un zéro mélangé avec la lettre O), ni une majuscule ou une minuscule échangée quelque part (Python est sensible à la casse).

Et rien.

Tout est niquel.

Alors pourquoi ça plante bordel de merde ?

Et bien ce message qui n’aide absolument pas peut venir du fait que les closures sont en lecture seule en Python. En résumé, vous avez essayé de faire un truc comme ça:

chose = 'truc'
def fonction():
    chose = 'machin'
    # ou chose += machin ou une variante

La solution est simple: ne modifiez pas chose. Si vous avez besoin de modifier son contenu, utilisez une variable intermédiaire:

chose = 'truc'
def fonction():
    bidule = chose
    bidule += 'machin' # je sais c'est bidon, c'est pour l'exemple

En Python 3.0, vous pouvez utiliser le mot clé nonlocal pour y palier: vous modifierez alors la variable du scope du dessus.

chose = 'truc'
def fonction():
    nonlocal chose
    chose += 'machin' # je sais c'est bidon, c'est pour l'exemple

Évitez d’utiliser global, qui a un fort potentiel d’effet de bord.

ImportError: cannot import name bidule et ImportError: No module named truc

Une fois que vous avez vérifié qu’un module existe bien avec ce nom (regardez de près, parfois c’est subtile), voici 3 possibilités:

Pas de fichier __init__.py

Un dossier n’est pas un module importable si il ne contient pas de fichier __init__.py. Vérifiez qu’il y en a un, et dans le cas contraire, créez en un vide.

Erreur de Python Path

Quand vous faites import bidule, bidule ne peut être importé que si le dossier qui le contient est dans le Python Path. Le Python Path est une variable qui contient une liste de dossiers dans lesquels chercher les modules à importer.

Le dossier courrant, le dossier contenant la bibliothèque standard de Python et le dossier où sont installés les bibliotèques Python de votre système d’exploitation sont automatiquement présents dans le Python Path.

Première chose: assurez-vous d’être à la racine du projet que vous lancez (erreur typique quand on utilise la commande ./manage.py avec Django par exemple).

Deuxième chose: si vous utilisez une bibliothèque qui n’est pas dans le Python Path (ça arrive assez souvent avec les tests unitaires: on éxécute les tests depuis le dossier de test, et le projet est dans un dossier à côté, donc pas dans le Python Path), vous pouvez ajouter manuellement un chemin dans le Python Path.

Pour se faire, avant l’import qui va foirer:

import sys
sys.path.append('/chemin/vers/le/dossier/parent/du/module/a/importer')

On peut tout à fait spécifier un dossier relativement au dossier courant. Il n’est pas rare d’ajouter le dossier parent du dossier courrant au Python Path:

import sys
import os

DOSSIER_COURRANT = os.path.dirname(os.path.abspath(__file__))
DOSSIER_PARENT = os.path.dirname(DOSSIER_COURRANT)
sys.path.append(DOSSIER_PARENT)

Par exemple, souvent dans le dossier d’un projet Django je fais un sous-dossier ‘apps’, puis je rajoute ceci au fichier settings.py:

import sys
import os

PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(PROJECT_DIR, 'apps'))

Il y a deux avantages à cela:

  • Mes applications sont regroupées dans un dossier et pas en vrac à la racine du projet, mais je peux quand même les importer en faisant import nom et pas import apps.nom.
  • J’ai maintenant une variable PROJECT_DIR que je peux utiliser partout, notamment pour définir où sont certains dossiers comme le dossiers des fichiers statiques:
STATIC = os.path.join(PROJECT_DIR, 'static')

Imports circulaires

Si vous importez poisson.rouge dans force.py, et force.bleu dans poisson.py, vous aurez aussi ce message d’erreur (qui n’aide pas beaucoup, on est d’accord).

Il n’y a pas vraiment de façon élégante de s’en sortir, c’est une des plus grosses couillasses en Python.

Solution 1: vous refactorez votre code pour avoir bleu et rouge dans un fichier couleur.py, lequel est importé dans poisson.py et force.py. C’est propre, mais parfois ça n’a aucun sens, et parfois ce n’est juste pas possible.
Solution 2: vous mettez l’import dans une fonctions ou une méthode dans un des deux modules (n’importe lequel):

def make_bouillabaisse():
    from poisson import rouge

C’est moche, mais c’est facile. Et je le répète, je n’ai jamais vu quelqu’un en 10 ans de Python proposer une solution élégante à ce problème. C’est un What The Fuck d’or.

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

Arf. L’erreur à la con. Parce que généralement elle vient du fait que l’on ne comprend pas vraiment ce qu’on fait. Or difficile de résoudre un problème quand on ne comprend pas de quoi il est question. Ne vous sentez pas mal, on s’est tous retrouvé comme un demeuré devant un problème d’encodage.

A noter que ce n’est pas une erreur spécifique à Python, mais si vous venez d’un langage comme PHP qui passe silencieusement ce genre d’erreur et affiche en prod des texts illisibles, voire une grosse erreur à l’écran peut surprendre.

Voici des causes très fréquentes:

Encodage du fichier.py

Comme il peut y avoir 1 million de possibilités, forcez vous à:

– TOUJOURS avoir votre éditeur de texte réglé pour utiliser UTF-8. Surtout sur Windows. Si votre chef vous l’interdit parce que “ça pose des problèmes d’encodage” (sic), quittez votre job (meilleur choix) ou faites vous former pour comprendre comment marchent les encodages et travailler dans cet environnement hostile.
– TOUJOURS avoir votre encodage (UTF-8 j’ai dis !) déclaré en haut du fichier.py: # -*- coding: utf-8 -*-

Vérifiez que les textes en entrée sont dans l’encodage prévu

Le contenu des bases de données ne sont parfois pas dans l’encodage déclaré de la table ou de la base. Le contenu d’une page HTML n’est parfois pas encodé dans l’encodage déclaré dans le HEAD. Le contenu d’un fichier n’est parfois pas encodé dans l’encodage par défaut de votre OS.

Il n’y a pas de secret. Pas de moyen infaillible de détection automatique. Il faut vérifier.

Vous confondez encodage et décodage (Python 2.7 et moins)

En Python, on DECODE pour passer d’un texte en encodé (UTF8, ISO-8859, CP1552, etc) et donc de type ‘str’ c’est à dire un flux de bits, à un texte unicode, une représentation interne, un objet non imprimable. Il est recommandé de décoder tout texte venant d’une source extérieur à votre programme, pour tout uniformiser.

A l’inverse, on ENCODE pour passer du type ‘unicode’ à un type ‘str’. Il obligatoire d’encoder un texte pour le communiquer au monde extérieur. Si vous ne le faites pas manuellement, Python le fera automatiquement, en essayant de deviner. Il n’est pas excellent à deviner.

En résumé:

In [7]: texte = open('/etc/fstab').read() # ou un téléchargement, ou une requete SQL...
In [8]: type(texte)
Out[8]: str
In [9]: texte = texte.decode('UTF8')
In [10]: type(texte)
Out[10]: unicode
In [11]: print texte # encode automatiquement le texte car votre terminal ne comprend qu'un text encodé
# /etc/fstab: static file system information.
#
[.............]
In [12]: type(texte.encode('UTF8')) # à faire avant de faire un write
Out[12]: str

Si ça continue de foirer, prenez tous les fichiers de votre application un par un: changez toutes les strings en unicode (les précéder d’un “u”), assurez vous que tout ce qui entre est converti en unicode (unicode() après urllib, open, etc) et tout ce qui sort est converti dans un encodage adapté (souvent UTF8) (encode(‘UTF-8’) avant send(), write() ou print).

Si ça ne marche toujours pas, embauchez un mec comme moi qui est payé cher pour se taper la tête contre les murs à la place des autres.

TypeError: ‘int’ object has no attribute ‘__getitem__’ et autres erreurs sur les tuples

Tuples d’un seul élément

CECI N’EST PAS UN TUPLE: (1)

Ceci est un tuple: (1,)

>>> type((1))

>>> type((1,))

>>> t = (1,)
>>> t[0]
1
>>> t = (1)
>>> t[0]
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'int' object has no attribute '__getitem__'

Et il y a plus vicieux:

>>> a = ("12345")
>>> b = ("12345",)
>>> a[0]
'1'
>>> b[0]
'12345'

C’est très dur à débugguer car on dans les deux cas il n’y a pas d’erreur étant donné que c’est une opération tout à fait légitime.

Concaténation automatique

Python vient avec une fonctionnalité qui concatène automatiquement les descriptions littérales de chaînes de caractères:

>>> "Ceci est un"                                  " test"
'Ceci est un test'

C’est très pratique pour les chaînes longues:

>>> print ("Ceci est une chaîne longue "
... "et je peux la diviser sur plusieurs lignes"
... " sans me fouler")
'Ceci est une chaîne longue et je peux la diviser sur plusieurs lignes sans me fouler'

Mais si vous oubliez une virgule dans un tuple (par exemple dans INSTALLED_APPS dans le fichier de settings.py de Django):

>>> REGLES = (
...     "Ne jamais parler du fight club",
...     "Ne jamais croiser les effluves",
...     "Ne jamais appuyer sur le petit bouton rouge" # <===== virgule oubliée !
...     "Ne jamais goûter"
... )
>>> print REGLES[3]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: tuple index out of range
>>> print REGLES[-1]
Ne jamais appuyer sur le petit bouton rougeNe jamais goûter

Le fichier/la liste est vide

On ne peut lire qu’une seule fois les générateurs en Python.

Si vous faites:

toto = (blague.title() for blague in histoire)

ou

toto = open('histoire.txt')

Et ensuite:

for blague in toto:
    print toto

len(list(toto))

La dernière ligne ne marchera pas. Toto aura été vidé par la première boucle for. Si vous souhaitez utiliser plusieurs fois le résultat de votre générateur, il faut le transformer en liste:

toto = list(toto)
for blague in toto:
    print toto

len(list(toto))

Attention, car vous avez maintenant l’intégralité des données chargées en RAM.

TypeError: ma_function() takes exactly x argument (y given)

Cette erreur est très explicite, et la plupart du temps ne pose aucun problème: vérifiez que vous passez le bon nombre d’arguments à la fonction. Faites particulièrement attention si vous utilisez l’opérateur splat.

Il existe néanmoins un cas particulier un peu taquin:

>>> class Americaine(object):
...     def dernier_mot(mot):
...         print mot
... 
>>> homme_le_plus_classe_du_monde = Americaine()
>>> homme_le_plus_classe_du_monde.dernier_mot("Monde de merde !")
Traceback (most recent call last):
  File "", line 1, in 
TypeError: dernier_mot() takes exactly 1 argument (2 given)

On définie une seul argument (mot) et on en passe un seul ("Monde de merdes !"), alors pourquoi Python n’est pas d’accord ?

C’est parce que l’on déclare une méthode sans self dans la signature. Or Python va passer automatiquement (et de manière invisible) la référence à l’objet courrant en premier argument, du coup la méthode reçoit deux arguments: la référence à homme_le_plus_classe_du_monde et "Monde de merde !". Ca ne marche pas puisque la méthode est déclarée pour n’accepter qu’un seul argument.

Il y a deux solutions. La plus simple, ajoutez self:

>>> class Americaine(object):
...     def dernier_mot(self, mot):
...         print mot
... 
>>> homme_le_plus_classe_du_monde = Americaine()
>>> homme_le_plus_classe_du_monde.dernier_mot("Monde de merde !")
Monde de merde !

Une seconde solution consiste à déclarer une méthode statique. Du coup on a plus besoin d’instance:

>>> class Americaine(object):
...     @staticmethod
...     def dernier_mot(mot):
...         print mot
... 
>>> Americaine.dernier_mot("Monde de merde !")
Monde de merde !

Ma structure de données par défaut n’est pas la bonne

Piège classique en Python, qu’il est important de répéter encore et encore tant il est la source de frustration chez les personnes qui ne le connaissent pas.

>>> from random import choice
>>> def bioman(forces=['rouge', 'bleu', 'vert', 'rose', 'jaune devant, marron derriere'], invite=None):
...     if invite is not None:
...         forces.append(invite)
...     return choice(forces)
... 
>>> bioman()
'rose'
>>> bioman()
'rouge'
>>> bioman(invite='magenta a pois gris')
'vert'
>>> bioman()
'jaune devant, marron derriere'
>>> bioman() # WTF ??????????
'magenta a pois gris'

Dans le dernier appel ‘magenta a pois gris’ est tiré au sort alors qu’on ne l’a pas passé en paramètre. Comment cela est-il possible ?

Cela vient du fait que les paramètres par défaut sont initialisés une seule fois pour tout le programme: dès que le module est chargé.

Si vous utilisez un objet mutable (liste, set, dico) et que vous le modifiez (ici avec append), le prochain appel de la fonction utilisera toujours la référence de cet objet, et donc de sa versio modifiée.

La solution est soit de ne pas utiliser d’objet mutable (tuple, strings, int, etc), soit de ne pas modifier l’objet:

>>> def bioman(forces=('rouge', 'bleu', 'vert', 'rose', 'jaune devant, marron derriere'), invite=None):
...     if invite is not None:
...         forces += (invite,) # ne modifie pas l'ancien objet
...     return choice(forces)

Ou alors (et ceci est souvent utilisé même si c’est moche):

>>> def bioman(forces=None, invite=None):
...     if forces is None:
...        forces = ['rouge', 'bleu', 'vert', 'rose', 'jaune devant, marron derriere']
...     if invite is not None:
...         forces.append(invite)
...     return choice(forces)

Toutes les parties qui sont éxécutées à l’inialisation du code (en opposition à celles qui le sont à l’appel du code) sont concernées par ce problème: les paramètres par défaut, les variables à la racine des modules, les attributs de classe déclarés en dehors d’une méthode, etc.

ItZ naute a beuhgue, Itse fitiure

Néanmoins cela a aussi son utilité. On peut en effet l’utiliser pour partager des états:

class Model(object):
    _pool = {
        'mysql': MySQL().connect('test'),
        'sqlite': Sqlite.open('test.db')
    }
    default_connection = 'mysql'

    def query(self, connection=default_connection, *params):
        connection.super_query(*params)

Et vous avez maintenant une classe de modèle qui gère plusieurs connections. Si vous l’étendez, les enfants de la classe et toutes les instances partageront le même objet connection, mais tout le reste sera unique à chacun d’eux. Cela évite un effet de bord du singleton qui oblige à partager un état et une identité. Ici on ne partage que la partie de l’état que l’on souhaite, et pas l’identité.

On gagne sur les deux tableaux: si on update la connection MySQL (par exemple parcequ’on a détecté qu’elle était stale), toutes les instances ont accès à l’objet modifé. Mais si on veut overrider la connection pour une seule classe, on peut le faire sans affecter les autres simplement en remplaçant l’objet à la déclaration de la classe.

On peut aussi utiliser cette fonctionnalité pour créer un cache. On appelle ça “mémoiser”:

def fonction_lente(param1, param2, _cache={}):
    # les tuples peuvent être des clés de dico \o/
    key = (param1, param2)
    if key not in _cache:
        _cache[key] = process_lent(param1, param2)
    return _cache[key]

Tous les résultats sont alors stockés en mémoire vive.

]]>
http://sametmax.com/quelques-erreurs-tordues-et-leurs-solutions-en-python/feed/ 23 995