PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Comment utiliser IndexError ? 10

samedi 17 janvier 2015 à 11:20

Après un peu de rodage, IndexError est sur les rails : design amélioré, meilleur perfs, workflow plus clair, etc.

Néanmoins on a été très surpris avec Max : la plupart des utilisateurs n’ont aucune idée de comment on s’en sert.

Pourtant c’est un clone de Stackoverflow, un site tellement célèbre qu’il est dans la première page des résultats de Google pour toutes les recherches techniques.

Pourtant c’est un simple site de question/réponse.

Pourtant on passe régulièrement dans les commentaires pour expliquer le principe.

Malgré ça, les gens se mélangent les pinceaux, ou ont peur de mal faire, ou ne savent trop par où commencer.

Cela prouve une fois de plus ce que j’arrête pas de répéter : l’évidence n’existe pas.

A quoi sert IndexError ?

A obtenir une réponse à sa question sur Python et son écosystème. C’est tout.

Pas à lancer un débat. Pas à discuter ou socialiser.

Le but est très simple : rendre service à celui qui pose la question maintenant mais aussi à ceux qui se la poseront dans le future.

Sur le site, ça se traduit par avoir pour chaque question une page avec :

[Titre de la question en gros]

| Détails de la question pour
| mieux comprendre le contexte

[Solution la plus probable]

[Solution un peu moins probable]

[Solution encore un peu moins probable]

[Solution encore un peu moins probable]

[Solution encore un peu moins probable]

Etc.

Comment on obtient ce résultat ?

D’abord en virant tout l’enrobage. Pas de “bonjour”, “merci”, “je suis débutant”, etc. On pose la question, on donne une réponse.

Ensuite, en ne se souciant pas d’avoir tort.

Si vous pensez qu’une question est stupide, vous pouvez la poser.

Si vous n’êtes pas certain que votre réponse est la bonne, vous pouvez la donner.

Si vous pensez que la réponse est sur Google, vous pouvez la poser.

Pour citer un commentaire célèbre sur Stackoverflow :

Chercher sur ce site plutôt que Google, c’est comme fait un accès hashmap plutôt que de parcourir une linked list.

C’est le but : avoir toutes les questions possibles, et les réponses possibles à cette question.

En clair, si quelqu’un vous dis que votre question est conne ou vous dis de chercher sur Google dans un commentaire sur IndexError, vous pouvez l’envoyer se faire foutre.

IndexError est là pour faire gagner du temps. Être efficace. Résoudre des problèmes.

Ça ne vous dispense pas de rechercher si la question n’a pas déjà été posée sur le site avant de la poser, bien entendu.

Commentaire VS Réponse

Il y a beaucoup de confusion sur quand commenter, et quand répondre.

Si vous êtes l’auteur de la question, vous voudrez presque toujours commenter. Il est très rare de répondre à sa propre question. C’est possible, et même encouragé si vous avez trouvé la réponse, mais c’est rare.

Si vous n’êtes pas l’auteur de la question :

Ex :

Il va falloir que tu nous poste ton fichier de config. => commentaire.

Remplace True par False pour voir ce que ça fait => réponse.

Et c’est la l’astuce : vous n’avez pas besoin d’être certain que votre réponse est la bonne pour la poster.

Les votes sont faits pour ça.

Postez toutes les solutions qui vous viennent en tête comme réponse, pas comme commentaire.

Si ce n’est pas la solution appropriée, la bonne sera votée, montera dans le classement et passera devant la votre donc ce n’est pas grave. Mais plus tard, un mec peut passer, chercher la réponse à la question, et voir que VOTRE réponse est SA solution.

Votez

Pour que ça marche, il faut donc voter. Si une question est pertinente, votez la. Si une réponse est pertinente, votez la. Même si vous n’êtes pas l’auteur de la question. Voter fait parti intégrante de la participation au site.

La plupart du temps, on upvote, les quelques raisons possibles de downvote :

Tout vote est annulable, donc n’ayez pas peur.

De même, si une réponse est la solution à votre question, cliquez sur le symbole ✓ à gauche de la réponse pour l’accepter. Ceci la propulse tout en haut de la liste des réponses, et change son style pour qu’elle soit plus visible.

En prime, toutes ces actions donnent des points, et les points sont importants, ce ne sont pas de simples gommettes pour gonfler l’égo des enfants de maternelle.

Les points

Ils servent à 2 choses.

D’une part, à permettre d’identifier les participants du site selon leur réputation. Si un réponse ou un commentaire est fait par quelqu’un de haute réputation, on peut se dire qu’on va y prêter plus d’attention qu’aux autres. C’est un gain de temps.

Mais surtout, les points sont attachées à des permissions.

En effet, sur IndexError, une autre personne peut éditer VOS questions et commentaires, comme sur un wiki. Le droit de faire cela ou non est lié au nombre de points qu’il a.

Voter donne des points. Poser des questions donne des points. Donner des réponses donne des points. Accepter des réponses donne des points.

Je me te tâte à arrêter le tuto Angular 34

jeudi 15 janvier 2015 à 05:25

Malgré les critiques, j’aime beaucoup AngularJS. Mais à l’annonce de la version 2 d’Angular complètement incompatible avec la version 1, seulement quelques années après sa sortie, des questions se sont posées sur l’avenir du framework.

J’utilise AngularJS pour certains de mes projets, je suis payé pour former sur AngularJS et j’ai commencé à écrire un guide sur le sujet.

Mais je me demande franchement si ça vaut le coup de s’investir plus.

Je vais continuer à l’utiliser pour mes projets actuels, tout en regardant du côté de la concurrence ici et .

Si j’écris l’article, ce n’est pas pour vous alarmer. Ni même pour vous dire “arrêtez d’utiliser Angular”. Je pense que la V1 va être maintenue par la communauté pendant des années.

Non, je me demande simplement si ça vaut le coup que je me casse le cul à finir le guide.

J’ai du travail à la pelle, le guide sur les tests en Python, Django une app à la fois, 0bin à porter en Python 3…

Bref, est-ce que vous êtes toujours aussi intéressés par ce guide ?

Embeder Python dans du C ou C++ 6

mercredi 14 janvier 2015 à 04:21

L’implémentation de référence de Python est écrite en C, et son API est exposée et bien documentée. Il est donc possible de créer des objets Python, charger un module Python ou exécuter une fonction Python depuis un code C/C++ et compiler tout ça.

Mettons que j’ai dans un fichier biblio.py :

def yolo(arg):
    return arg.upper() + ' !!'

Je peux écrire un fichier prog.c qui l’utilise :

#include <Python.h>
 
int main () {
    // PyObject est un wrapper Python autour des objets qu'on
    // va échanger enter le C et Python.
    PyObject *retour, *module, *fonction, *arguments;
    char *resultat;
 
    // Initialisation de l'interpréteur. A cause du GIL, on ne peut
    // avoir qu'une instance de celui-ci à la fois.
    Py_Initialize();   
 
    // Import du script. 
    PySys_SetPath("."); // Le dossier en cours n'est pas dans le PYTHON PATH
    module = PyImport_ImportModule("biblio");
 
    // Récupération de la fonction
    fonction = PyObject_GetAttrString(module, "yolo");
 
    // Création d'un PyObject de type string. Py_BuildValue peut créer
    // tous les types de base Python. Voir :
    // https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
    arguments = Py_BuildValue("(s)", "Leroy Jenkins"); 
 
    // Appel de la fonction.
    retour = PyEval_CallObject(fonction, arguments);
 
    // Conversion du PyObject obtenu en string C
    PyArg_Parse(retour, "s", &resultat);
 
    printf("Resultat: %s\n", resultat);
 
    // On ferme cet interpréteur.
    Py_Finalize(); 
    return 0;
}

Là je mets du C, mais la seule vraie différence avec du C++, c’est qu’on aurait cout au lieu de printf.

Pour compiler tout ça, il faut les headers Python et un compilateur. Sous Ubuntu, c’est un simple :

sudo apt-get install python-dev gcc

Et on a tout nos .h dans /usr/include/python2.7. On gccise :

gcc -I/usr/include/python2.7 prog.c -lpython2.7 -o prog -Wall  && ./prog

Même pas un warning, c’est beau.

./prog
Resultat: LEROY JENKINS !!

C’est un exemple simple, mais comme vous le savez je suis une grosse burne en C, donc je ne pourrai pas porter l’expérience plus loin.

Je ne le recommande pas pour rendre votre programme scriptable en Python. Il vaut mieux permettre à Python d’appeler votre code C dans ce cas. Des outils comme cffi rendent cela beaucoup plus facile et rentable que tout faire tout le taff à la main. D’ailleurs si quelqu’un est chaud pour faire un tuto sur cffi…

Non, c’est plus dans le cas où vous avez un programme C, un programme Python, et votre programme C veut utiliser le programme Python sans avoir à tout réécrire. Ou pour le cas où vous avez décidé d’écrire une grosse partie de votre programme en Python pour profiter de sa productivité, mais que vous ne pouvez pas installer Python sur la machine sur laquelle vous aller installer le programme. Bon, y a PyInstaller et Nuitka pour ça également hein, donc tentez avant de tout embeder comme un bourrin.

Ça reste intéressant de voir les entrailles de CPython et à quel point il joue bien avec les langages bas niveaux.

Rendez votre package exécutable avec __main__.py 5

mardi 13 janvier 2015 à 07:47

Tout le monde connait le fichier __init__.py, mais __main__.py est moins connu.

Il permet de lancer du code si on tente d’exécuter un package (c’est à dire un dossier qui contient un fichier __init__.py):

$ tree monpackage/
monpackakge/
├── __init__.py
└── __main__.py
 
0 directories, 2 files
$ cat monpackage/__main__.py
print('Hello :)')
$ python monpackage/ # ceci est un dossier 
Hello :)

Le __main__.py est aussi exécuté quand on fait python -m monpackage.

Notez que son exécution suppose l’import préalable du package, et que donc __init__.py sera toujours exécuté avant __main__.py. En revanche, faire juste import monpackage ne déclenche pas l’exécution de __main__.py.

Si vous zippez votre package et appelez la commande python, c’est aussi ce fichier qui sera exécuté. Pratique donc, pour faire un exécutable portable à peu de frais, tout en gardant la lib importable.

Qu’est-ce que le duck typing et à quoi ça sert ? 5

lundi 12 janvier 2015 à 03:47

Le duck typing, qu’on pourrait traduire par “typage canard” mais on ne le fera pas parce que c’est très moche, est une manière de créer des APIs basée sur la philosophie que l’aspect pratique est plus important que la pureté du code.

L’idée est de créer des signatures de callable qui acceptent des paramètres en fonction de leur comportement, pas leur type :

Si ça marche comme un canard et que ça fait le bruit d’un canard, alors ça ressemble assez à un canard pour le traiter comme un canard

Imaginez que vous ayez un objet avec un interface ICanard:

class ICanar:
    def coin():
        pass

Une fonction qui est programmée selon le duck typing acceptera comme argument un objet qui possède la méthode coin, peut importe si il implémente cette interface ou non.

En gros, si un paramètre possède une interface suffisante pour nous, ou peut être casté en un objet avec une interface suffisante pour nous, on l’accepte. Cela rend un callable plus générique.

Ok, trève de bavardage, qu’est-ce que ça implique, dans la vraie vie vivante ?

Si je fais une fonction qui retourne le premier élément d’une liste ou un élément par défaut :

def getfirst(lst, default=None):
    try:
        return lst[0]
    except IndexError:
        return default

Pratique, et ça marche sur d’autres itérables :

>>> getfirst([1, 2, 3])
1
>>> getfirst('abcde')
'a'

On a une forme de duck typing : si on peut récupérer le premier élément, alors ça suffit pour nous. Peut importe qu’il s’agit d’une liste ou d’un tuple.

On peut néanmoins améliorer la généricité de cette fonction:

def getfirst(iterable, default=None):
    for x in iterable:
        return x
    return default

Ici, le comportement recherché est qu’on puisse faire une une boucle for dessus, pas qu’on puisse récupérer un élément par son index.

Cela rend la fonction encore plus flexible, ainsi elle marche sur les générateurs, les flux, les fichiers:

>>> getfirst(open('/etc/fstab'))
'# /etc/fstab: static file system information.\n'

Un autre exemple ? La fonction Python sum par exemple, accepte tout types de nombres :

>>> sum((1, 2, 3)) # integers
6
>>> sum((1.3, 2.4, 3.5)) # floats
7.2
>>> sum((1j, 2j, 3j)) # complexes
6j

Sympas, mais l’addition en Python supporte bien plus que les nombres :

>>> [1] + [2]
[1, 2]
>>> (1, 2) + (2, 4)
(1, 2, 2, 4)
>>> "a" + "b"
'ab'

Mais sum ne les accepte pas :

>>> sum("a", "b")
Traceback (most recent call last):
  File "<ipython-input-24-1e5baeda1183>", line 1, in <module>
    sum("a", "b")
TypeError: sum() can't sum strings [use ''.join(seq) instead]

Il est possible de faire un sum plus générique :

def sumum(*iterable, start=None, default=None):
    # On donne à l'utilisateur la possibilité
    # de passer un premier élément
    if start is None:
        # on récupère le premier élément
        try:
            start, *iterable = iterable
        except ValueError:
            # Il n'y a aucun élément dans l'itérable
            # donc on retourne la valeur par default
            return default
    # on additionne
    for x in iterable:
        start += x
    return start

Le duck typing, à son maximum :

>>> sumum('a', 'b', 'c')
'abc'
>>> sumum([1, 2], [3, 4])
[1, 2, 3, 4]

Le duck typing implique aussi une prise de décision. Qu’est-ce qui serait le plus pratique ? De pouvoir additionner tous les types additionnables ? Ou de pouvoir additionner n’importe quoi qui ressemble à un nombre ?

Imaginons que la plupart de nos libs, plutôt que de fournir la possibilité d’additionner, propose la possibilité de caster vers un float :

class Temperature:
    def __init__(self, value, unit='C'):
        self.value = float(value)
        self.unit = unit
    def __float__(self):
        if self.unit == 'C':
            return self.value
        if self.unit == 'K':
            return self.value - 273.15
        if self.unit == 'F':
            return (self.value - 32) * 5/9
    def __repr__(self):
        return '%s %s' % (self.value, (self.unit != 'K')*'°'+self.unit)
 
t1 = Temperature(5)
t2 = Temperature(3, 'K')
t3 = Temperature(30, 'F')
t1, t2, t3
## (5.0 °C, 3.0 K, 30.0 °F)

Dans ce cas notre fonction pourrait convertir tous les éléments d’un itérable avant addition :

def sumcast(*iterable, start=None, default=None):
    # On donne à l'utilisateur la possibilité
    # de passer un premier élément
    if start is None:
        # on récupère le premier élément
        try:
            start, *iterable = iterable
        except ValueError:
            # Il n'y a aucun élément dans l'itérable
            # donc on retourne la valeur par default
            return default
    # on additionne en convertissant tout en float
    start = float(start)
    for x in iterable:
        start += float(x)
    return start
 
>>> sumcast(1, "3", t1, t2, t3)
-262.26111111111106

Dans tous les cas, on se fiche complètement que nos objets soient d’un type précis ou qu’ils implémentent une interface précise à partir du moment où leur API est suffisamment proche du ce type ou de l’interface dont on a besoin.

Le duck typing a beau être une pratique vouée à simplifier la vie au prix du formalisme, il ne dispense pas de documenter votre code à propos de cette subtilité afin que l’utilisateur final n’ait pas de mauvaise surprise.

Il convient de ne pas abuser du duck typing, qui est là pour rendre service uniquement. Si vous ajoutez des cas farfelus dans votre code pour supporter des situations rares, vous le rendez plus compliqué et moins robuste. Visez la généricité pour les situations les plus courantes, pas toutes les situations possibles.

Et souvenez-vous que plus on est dynamique sur les types, plus on perd en performance. Il faut savoir quelle part de compromis on est prêt à faire.