PROJET AUTOBLOG


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

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

⇐ retour index

Mise à jour

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

Qu’est-ce que les websockets et à quoi ça sert ? 8   Recently updated !

mardi 30 décembre 2014 à 05:51

Le protocole WebSocket vise à développer un canal de communication full-duplex sur un socket TCP.

LOL. C’est clair non ?

Vous inquiétez pas, tonton Sam est là.

Le Web a évolué. On est passé de Gopher a HTTP 1 puis 1.1. Et on a eu AJAX pour rafraîchir la page sans tout recharger.

Et maintenant on a des apps complètes qui font des centaines de requêtes au serveur alors même que l’utilisateur ne change pas de page. D’ailleurs, je parie que plein de gens ne savent même plus ce qu’est une page…

Le problème c’est qu’AJAX, c’est toujours HTTP, et HTTP est sans état (stateless) : il ne garde aucune information en mémoire d’une requête à l’autre. Ça a des avantages, mais cela implique qu’à chaque requête, il faut ouvrir une connexion et la refermer. Ce qui bouffe quelques ms à chaque fois, et d’autant plus si on utilise SSL.

Une autre limite, c’est que le serveur ne peut pas envoyer de données au client (ici le navigateur) si le client ne fait pas une requête au préalable. Du coup, pour savoir si il y a quelque chose de nouveau, le navigateur doit régulièrement faire des requêtes au serveur ou utiliser des gros hacks comme le long polling.

Les websockets (c’est un abus de langage, on devrait parler du protocole Websocket) ont été créés pour répondre à ces besoins : elles permettent d’ouvrir une connexion permanente entre le navigateur et le serveur. Ainsi, chaque requête est plus rapide, et plus légère. En prime, le serveur peut envoyer des requêtes au navigateur pour le prévenir qu’il y a du nouveau.

Ceci permet de faire tout ce que permettait de faire AJAX mais en plus rapide, et en plus léger. Et également d’envoyer des notifications (ce contenu a changé, un message est arrivé, l’autre joueur a fait cette action…) au navigateur au moment où l’événement se produit.

En gros, de faire des apps Web quasi temps réel.

Il existe d’autre technos pour faire cela : applets Java, flash, comet, server sent events…

Mais aucune n’ont décollé. Websocket est donc aujourd’hui la solution de facto.

Caractéristiques

Le protocole Websocket utilise l’abréviation ws et wss si SSL, les URLs vers des endpoints websocket ressemblent donc à : ws://domaine.tld/chemin/vers/truc/.

Intelligemment, il utilise un handshake compatible avec celui de HTTP, permettant à un serveur de gérer les deux sur les mêmes ports. Donc on peut faire du Websocket sur le port 80 et 443. Néanmoins, certains proxy se gourent quand ils voient du websocket non chiffré et gauffrent votre connexion en la traitant comme du HTTP. Donc si vous voulez une app solide, investissez dans un certif SSL.

Tout ça fonctionne à partir de IE10. Notez comme IE est devenu le standard de ce qui ce fait de moins bien à tel point que je n’ai même pas besoin de vous parler des autres, vous savez que ça marche. Il existe en plus des plugins flash pour simuler des websockets sur les navigateurs anciens, c’est à dire les encore plus vieux IE.

Par défaut, les websockets permettent de faire de requêtes crossdomain, contrairement à AJAX. Avec les nouvelles apps qui utilisent NodeJS en local (comme popcorntime) on peut imaginer une nouvelle type d’attaque : une page web qui se connecte à un serveur websocket local de votre machine. Comme les websockets sont souvent utilisées pour du RPC, il y a du potentiel.

Bon, ta gueule, et montre le code

Vous noterez que ce qui prend du temps dans l’exemple c’est la connexion, qu’on ne fait qu’une fois. Ensuite l’échange de données est super rapide.

Ceci est un exemple Javascript, mais un client websocket n’est pas forcément un navigateur. En fait, c’est très précisément le cas avec WAMP, dont les clients peuvent être des programmes Python, Objective C, Java, C++, etc. L’avantage de WAMP, c’est qu’il automatise toute la machinerie pour découper la logique de son programme en divers fonctions et services, plutôt que d’avoir à tout faire à la main avec send() et onmessage().

Dans tous les cas, il vous faudra un serveur qui supporte les Websockets pour l’utiliser. En Python, c’est Tornado ou Twisted (sur lequel est basé le serveur WAMP crossbar). En Javascript, c’est NodeJS. Quoi qu’il en soit, il vous faut un logiciel qui gère l’IO de manière non bloquante, car il y a de nombreuses connexions ouvertes en simultanées, si on veut que ça soit performant.

Bridge HTTP/WAMP 5

lundi 29 décembre 2014 à 11:07

Il y a 4 gros freins à l’adoption de WAMP :

  1. L’incompréhension de la techno. Les gens ne voient pas à quoi ça sert, ce qu’ils peuvent faire avec, et ont peur. Logique. Je vais pas les blâmer, c’est pareil avec toute nouvelle techo. S’investir dans un nouveau truc a un coût, surtout un truc jeune.
  2. La doc. je vais travailler sur la question, mais c’est un travail de fond.
  3. L’API : ça va avec les deux points plus haut. Il faut quelque chose de plus haut niveau. L’API flaskesque est un bon début, il faut maintenant continuer dans ce sens.
  4. L’intégration avec les anciennes technos. Par exemple, un site Django. Personne n’a envie de mettre toute son ancienne stack à la poubelle.

Toute les libs qui introduisent une nouvelle façon de travailler rencontrent ce problème. Quand les ORM sont sortis, c’était pareil. Quand les Django et Rails, et Symfony sont sortis, c’était pareil.

Mais puisqu’on le sait, on peut agir.

Les 3 premiers points ont déjà un début de solution, ce qui nous intéresse c’est donc le 4ème point.

Il y a de nombreuses choses à faire pour l’intégration : authentification, actions bloquantes, communications…

On ne peut pas tout résoudre d’un coup, mais une solution qui ratisse large serait de créer un bridge HTTP/WAMP.

Le principe : faire un client WAMP qui soit aussi client HTTP avec une API REST.

Fonctionnement

En envoyant des requêtes HTTP avec un webtoken pour s’authentifier, on peut faire un register/subscribe/call/publish sur le bridge en spécifiant une URL de callback. Le bridge transmet tout ça à routeur WAMP. Quand un événement arrive sur le bridge qui concerne une des URLs de callback, il fait une requête sur l’URL avec les infos arrivées via WAMP.

API :

POST /register/

{
    // token d'authentification
    token: "fdjsklfqsdjm",
    // On enregistre des urls de callbacks pour chaque "function" exposées en RPC
    // Quand le bridge reçoit un appel RPC via WAMP, il fera une requête POST
    // sur la bonne URL. Votre app récupère les données via POST, et retourne
    // du JSON que le bridge va transmettre via WAMP.
    endpoints: {
        "nom_fonction_1":  "http://localhost:8080/wamp/rpc/nom_fonction_1/",
        "nom_fonction_2":  "http://localhost:8080/wamp/rpc/nom_fonction_2/"
    }
}

POST /subscribe/

{
    token: "fdjsklfqsdjm",
    // On enregistre des urls de callbacks chaque abonnement à un topic.
    // Quand le bridge reçoit un message PUB via WAMP, il fera une requête POST
    // sur la bonne URL. Votre app récupère les données via POST.
    endpoints: {
        "nom_fonction_1":  "http://localhost:8080/wamp/pub/nom_fonction_1/",
        "nom_fonction_2":  "http://localhost:8080/wamp/pub/nom_fonction_2/"
    }
}

POST /call/nom_fonction/

{
    token: "fdjsklfqsdjm",
    // Le bridge fera l'appel RPC, récupère la valeur de retour, et la renvoie
    // à cette URL via POST ou une erreur 500 en cas d'exception.
    callback: "http://localhost:8080/wamp/callback/nom_fonction",
    // Les params à passer à l'appel RPC
    params: ['param1', 'param2']
}

POST /publish/nom_sujet/

{
    token: "fdjsklfqsdjm",
    // Le bridge fera la publication WAMP
    // Les params à passer lors de la publication
    params: ['param1', 'param2']
}

On peut rajouter des fioritures : recharger le fichier de config qui contient les API keys, se désabonner, désinscrire un callback RPC, etc.

Bien entendu, du fait d’avoir un intermédiaire, c’est peu performant, mais suffisant pour permettre une communication entre des apps existantes et vos apps WAMP et mettre un pied dedans.

Intégration poussée

Ceci permet une intégration générique, de telle sorte que tout le monde puisse intégrer son app facilement avec quelques requêtes HTTP. Néanmoins, avoir un plugin pour son framework plug and play faciliterait la vie de beaucoup de gens.

Donc la partie 2, c’est de faire des apps pour les frameworks les plus courants qui wrappent tout ça.

Par exemple, pour Django, ça permettrait de faire :

settings.py

WAMP_BRIDGE_URL = "http://localhost:8181/"

urls.py

from wamp_bridge.adapters.django import dispatcher
 
urlpatterns += ('',
    url('/wamp/, dispatcher),
    ...
)

views.py

from wamp_bridge.adapters.django import publish, call, rpc, sub
 
@rpc()
def nom_fonction_1(val1, val2):
    # faire un truc
 
@sub()
def nom_topic_1(val1, val2):
    # faire un truc
 
def vue_normale(request):
    publish('sujet', ['arg1'])
    resultat = call('function', ['arg1'])

Ca répond pas à des questions du genre : “comment je fais pour garder mon authentification Django” mais c’est déjà super glucose.

Implémentation

On peut créer un bridge dans plein de langages, mais je pense que Python est le plus adapté.

Les clients JS utiliseraientt de toute façon nodeJS, qui n’a pas besoin de bridge, puisque déjà asynchrone. Un routeur en C demanderait de la compilation. PHP est trop moche. Java, c’est tout un bordel à setuper à chaque fois. C#, si il faut se taper Mono sous Linux…

En prime, le routeur crossbar est déjà en Python, donc a déjà tout sous la main pour le faire. On peut même penser à l’intégrer à crossbar plus tard histoire que ce soit batteries included.

Il faut une implémentation Python 2 et Python 3, donc une avec Twisted, et une avec asyncio.

Pour le client twisted, on peut fusionner le client WAMP ordinaire avec treq.

Pour asyncio, il y a aiohttp qui fait client et serveur.

On met les clés API dans un fichier de conf ou une variable d’env, une auth via token, et yala.

C’est le genre de projet intéressant car pas trop gros, mais suffisamment complexe pour être un challenge surtout qu’il faudra des tests unitaires partout, de la doc, bref un truc propre.

Je laisse les specs là, des fois qu’il y ait quelqu’un qui ait des envies de code cet hiver et cherche un projet open source dans lequel se lancer.

Si on se sent un peut foufou, on peut même transformer ça en bridget HTTP <=> anything, avec des backends pour ce qu’on veut : IRC, XMPP, Redis, Trigger Happy…

Prendre le contrôle d’internet ? C’est possible ! Dégainez & Tirez avec Trigger Happy 4

dimanche 28 décembre 2014 à 07:45

Ceci est un post invité de foxmask posté sous licence creative common 3.0 unported.

“C’est l’histoire d’un mec qu’est su’l’pont de l’Alma et regarde dans” … le python, et comme il débute, se demande mais putain de bordel, des projets à la con à pondre pour se lancer à l’assaut d’un langage, c’est toujours les mêmes trucs chiants que plus personne n’a envie de voir tels : forum, blog, wiki, cms. Alors comment être novateur un poil plus que ces projets sans (plus aucun) défit technique ?

A cette question je me suis dit, pourquoi ne pas produire “simplement” (toute proportion gardée) un équivalent libre au célébrissime IFTTT ?

Mais qu’est-ce ?

IFTTT est un service qui vous permet de brancher entre eux, les services internet où vous possédez un compte, comme Twitter, Facebook pour ne citer qu’eux (parce que la liste est longue comme un python au moins;) Et donc quand un évènement défini se produit sur votre compte twitter, genre un tweet de votre poto tombe, le service réagit au quart de tour pour rebalancer les données ailleurs, sur Facebook, un blog et n’importe quoi qui vous chante.

Hé bien cet équivalent libre est Trigger Happy, et ce “principe” décrit ci-dessus défini un ESB (Entreprise Service Bus, très connu du monde Java), un BUS qui récupère des données de droite et vous les expédie à gauche.

Wikipedia le défini ainsi :

L’enterprise service bus (ESB) est une technique informatique intergicielle. Son but est avant tout de permettre la communication des applications qui n’ont pas été conçues pour fonctionner ensemble

Pourquoi un tel projet ?

3 aspects :

  • Avec ce projet je me suis lancé le défit d’arriver à cerner au mieux Django et abordé au mieux Python.
  • Comme tout ce que j’entreprends, se produit par pur réaction allergique à un truc qui m’a gonflé, explications les zamis :
    Au début on fait (plus ou moins) comme tout le monde, sa veille techno à partir de flux RSS de sites web choisis, avec un lecteur de flux RSS moins bien choisi comme iGoogle à une certaine époque ou encore … Google Reader.
    Là le truc qui m’a pris le chou, ce fut d’avoir dans Evernote, le résultats des flux RSS de mes sites favoris complètement atrophiés.
    Du genre “il a été à l’école” devenait “il a t l’cole”. Or le problème ne vient pas d’Evernote qui affiche parfaitement nos chers caractères latins, mais de IFTTT qui les bouffait au passage. Outre IFTTT il existe des versions payantes de services mais qui sont vite très vite limitées dans le nombre de triggers déclenchés que sont Zapier et CloudWork.
  • Le dernier aspect concerne sa chère vie privé.
    Oui en effet, pour que IFTTT puisse récupérer des donnés d’un service à l’autre, il faut que vous ayez un compte sur chaque service comme Twitter et Facebook. Pourquoi IFTTT en a besoin ? Pour que, “en votre nom”, avec les accréditations que vous leur octroyez, puisse lire/écrire les informations de part et d’autre.
    Mais pourquoi faire confiance à IFTTT / Cloudwork / Zapier ? Pourquoi leur offrir NOS accréditations les yeux fermés ?
    Trigger Happy repond à la question puisque l’application vous appartient, les accréditations obtenues de Twitter, Facebook etc, sont stockées dans VOTRE application Trigger Happy. Et personne n’ira les exploiter pour voir ce que vous faites sur vos comptes.

Comment fonctionne le projet ?

Dans la version simplifiée ceci donne :

Trigger Happy wokring process

Image tirée de ma main “gauche” gauche, enfin la gauche pas adroite


De part et d’autres (dans les nuages) on a les services qui nous intéressent
Les éclairs oranges sont les connecteurs qui permettent de dialoguer avec les services
Au milieu .. (non ne coule pas une rivière) le moteur Trigger Happy qui gère, via les connecteurs, l’échange des données.

Dans la version complète ceci donne :

Trigger Happy Micro ESB

Trigger Happy Micro ESB

On a au milieu un “tube” (pipeline) qui va permettre, via des “command” & settings & url & service provider, d’identifier les services “django th 1, 2, 3, 4″.
A un moment donné, à l’entrée du tube, arrive un “flux” de donnés identifié pour admettons “django th 1″ aka un flux RSS (au pif) puis on identifie une destination, admettons “django th 2″ aka “twitter”, et les données collectées repartent donc du tube vers twitter, comme le service s’attend à les recevoir.

Dans la vraie vie comment ça se passe ?

Ça se passe bien mon zami : je m’en va (la fote est volontaire tout comme l’reste;) vous montrer la création d’un trigger, permettant d’extraire les billets du flux de mon blog et de les renvoyer sur Twitter en 5 étapes :

Ici en premier lieu vous pouvez voir la liste des services que Trigger Happy gere pour l’utilisateur courant (mézigues)

Trigger Happy : services activés

Trigger Happy : services activés

Puis l’accueil de l’appli où on remarquera que je suis radin en nombre de trigger affichés par page parce que … je me sers de l’appli via un browser sur mon smartphone m’sieur ‘dames :

Trigger Happy : Accueil

Trigger Happy : Accueil

Etape 1:

Trigger Happy : Etape 1 de la création d'un trigger

Choix du service fournissant les données

Etape 2:

Trigger Happy : Etape 2 de la création d'un trigger

nommage du service et fourniture de l’origine des données

Etape 3:

Trigger Happy : Etape 3 de la création d'un trigger

Choix du service accueillant les données

Etape 4:

Trigger Happy : Etape 4 de la création d'un trigger

Trigger Happy : Etape 4 de la création d’un trigger

Ensuite le moment venu, se déclenche ce trigger, et pour en voir le résultat, on peut consulter le billet que j’avais préparé le mois dernier pour une présentation Django Paris. Et qui au moment de la publication du billet, à 19h15 pétantes, est tombé sur twitter directement et Evernote dans la foulée.

Voilà !

Le défit est parti et n’attend plus qu’à ce que le nombre de services croissent. Pour cela rien de plus simple, j’ai fait une doc expliquant comment pondre un module django qui exploite le service de votre choix tel Buffer, Trello, Dropbox et j’en passe et des meilleurs. Tout ce dont on a besoin : l’API du service visé en python, créer un compte pour avoir accès au service et suivre le howto sur readthedoc

Last but not least aux dev : ca tourne avec django 1.7 / Python 2.7 et 3.4

Dernier détail: comme je suis sûr que vous vous demandez pourquoi ce nom de projet ? C’est un perso de la franchise Skylander auquel joue mon fils et comme le projet “trigger” à tirelarigot sur l’net, ca collait pile poil :)

Comment arrivent les plans à 3 ? 5   Recently updated !

samedi 27 décembre 2014 à 12:40

Au début, on se demande comment parler à l’autre. Et comment on fait pour toucher ? Embrasser ? Baiser ? Obtenir une fellation ? Une sodomie ? Amener un scénario, un jeu SM ?

Et puis on grandit et on passe de l’autre côté de la barrière, et à chaque fois on se dit que c’était pas si mystérieux que ça au final.

Mais il reste toujours des situations inconnues, qui apparaissent obscures : par exemple, comment on arrive à un threesome, bordel ?

Comme d’habitude, je n’ai pas de recette magique, mais je peux vous donner quelques exemples de situations.

Promotion sur canapé

Marc, Marie et Mathilde ont orienté le rétroprojecteur vers le plafond pour regarder un film comme des loques, allongés sur deux canapés poussés l’un contre l’autre. Marc et Marie sortent ensemble, Marc et Mathilde ont fleurté. Tous les 3 sont sexuellement décontractés et ont déjà parlé de cul à table. Ils se connaissent, ils ont plusieurs soirées derrière eux.

En plein film, Marc caresse le bras de Marie, puis descend lentement vers sa chatte, sans y penser. Il finit par la caresser discrètement, et emporté, lui cale un doigt. Marie reste calme, mais Mathilde s’en aperçoit. Elle soulève le shirt de Marie, et commence à lui toucher un sein, puis à le prendre dans sa bouche. Tout en regardant le film, Marc et Mathilde s’occupent de Marie. Puis l’excitation monte, et Marc retire le pantalon de Marie pour lui faire un cuni…

Que s’est-t-il passé ?

Les 3 personnes se connaissaient. Elles sont à l’aise sexuellement. Elles ont déjà parlé de sexe.

En effet, on ne peut pas faire ça avec tout le monde : seul un certain type de personnes se retrouvent dans ces threesomes, les gens à l’aise avec le cul. Si vous avez du mal à assumer votre sexualité, si pour vous la fellation est un événement rare, ne tentez pas le plan à 3 tout de suite. Courir avant de marcher, tout ça…

Ensuite, ils savent qui ils sont : ils se sont vu plusieurs fois, ont parlé de sexe, se sont évalués. La prise de risque est allégée.

Ils sont seuls, dans une situation confortable, intime, et tout escalade doucement, naturellement. Personne n’a essayé de lancer un plan à 3. Ça s’est fait, non pas parce que chaque pas à amené vers, mais parce qu’aucun pas n’a été bloqué.

Car quand il s’agit de sexe, si une envie n’est pas satisfaite, c’est toujours imposé par une forme ou une autre de blocage. Il ne s’agit donc pas de pousser, mais d’être dans un contexte fluide, sans blocage.

Comment on fait, putain ?

La putain, est aussi la porte d’entrée aux plan à 3. Julien et Jérémy vont dans un bordel, embauchent une professionnelle en lui demandant si ils peuvent être deux pendant la séance. Ils se rejoignent dans la chambre, surmontent leurs inhibitions et se déshabillent. Au début ils hésitent, mais la prostituée connaît son métier et commence par en sucer un, exposant sa croupe à l’autre. Invité, le second la pénètre après avoir mis un préservatif. Les deux amis ne se toucheront pas, nullement intéressés l’un par l’autre, mais trouveront agréable l’expérience de partager une même femme.

Encore une fois, on note que tout le monde ne peut pas se retrouver dans cette situation : il faut des gens à l’aise avec l’idée de prostitution, et qui acceptent de se dévoiler, plus nus que jamais, l’un à l’autre dans une activité sexuelle.

Ce sont des amis, ils se connaissent. On en revient donc à la notion de confort et de confiance.

De son côté, la femme, accepte de prendre deux hommes en même temps, ce qui n’est pas forcément toujours le cas. Croire que les putes acceptent tout et que la permission est optionnelle est l’erreur des gens qui confondent sexe payé et esclavage. Une prostituée est une femme ordinaire, qui mérite le même respect.

Je ne fais pas ça d’habitude

Ingrid, Isabelle et Ignace ont tous beaucoup bu. Ignace a été gogo danseur par le passé et a fait sérieusement monté la température sur scène, mais maintenant ils se détendent, assis sur le même canapé. Ils ne parlent pas, ils sont dans la torpeur que le mélange de musique forte, d’éthanol et d’activité physique finit toujours par produire.

Ignace a lancé cette soirée chez lui, comme de nombreuses autres. Tout le monde le connaît comme quelqu’un de sociable, connaissant beaucoup de gens. Car il aime ça, il aime parler à autrui. Il aime aider. Il aime recevoir. Et toutes les personnes ce soir le savent.

Pour ne rien gâcher, Ignace est beau gosse, officiellement bi, et parle de sexe ouvertement. Il sera donc le pivot.

Dans le flou flottant de cet instant, les deux filles se reposent sur Ignace qui en embrasse une sur la tête. Il met ses bras autour d’elles. Ils restent ainsi un moment qui peut paraître long (mais difficile à évaluer car on perd la notion du temps dans ces conditions), affectueusement. Les mains d’Ignace caressent légèrement Ingrid et Isabelle. L’une l’embrasse sur la joue. L’autre l’embrasse sur la main. Il peut se passer un temps important entre deux baisers. Mais il décroit, jusqu’à ce que leurs corps remuent un peu, légèrement, l’un contre l’autre. Alors ils s’en vont dans la chambre.

Une fois de plus : les personnes sont connues, le milieu est sûr, il y a du confort et de la confiance, et bien entendu une aisance avec le sexe. L’alcool et le bruit catalysent cela, donnant une impression de proximité, voir d’intimité.

Ou autres

Ce ne sont pas les seules possibilités. Je pourrais parler de la drogue, des clubs échangistes, des plans à 3 planifiés avec son/sa partenaire…

Mais l’idée reste là :

Ceci s’applique même pour l’exemple avec la prostituée. Si vous ne le voyez pas, c’est que vous avez encore un blocage quelque part. Le premier blocage à faire sauter, c’est le vôtre.

Une autre chose importante là-dedans, c’est qu’aucun des acteurs n’a la peur d’être rejeté. Non pas qu’ils ont la certitude que ça va marcher, mais plutôt :

Autre chose : personne n’a rien demandé. Ils font. Pour ce genre de choses, il est plus facile de demander pardon que la permission.

Qu’est-ce que l’unpacking en Python et à quoi ça sert ? 6

vendredi 26 décembre 2014 à 10:03

Ce terme apparaît dans de nombreux articles du blog, et je prends parfois le temps de l’expliquer superficiellement. Évidemment, à de nombreux moments j’ai fait des tutos en ayant la connaissance de l’unpacking comme prérequis, et rien vers quoi faire un lien. Corrigeons ça, en attendant que je traduise les slides sur WAMP.

Le principe de base

Normalement, si vous voulez mettre le contenu d’un tuple dans des variables, vous devez procéder ainsi :

>>> ducks = ('riri', 'fifi', 'loulou')
>>> duck1 = ducks[0]
>>> duck2 = ducks[1]
>>> duck3 = ducks[2]
>>> print(duck1)
'riri'
>>> print(duck2)
'fifi'
>>> print(duck3)
'loulou'

L’unpacking, qu’on pourrait traduire par le terme fort moche de “déballage”, dans le sens “ouvrir un colis”, permet de faire la même chose, bien plus facilement :

>>> duck1, duck2, duck3 =  ducks
>>> print(duck1)
'riri'
>>> print(duck2)
'fifi'
>>> print(duck3)
'loulou'

Il n’y a rien à faire, c’est automatique. La seule condition est que le nombre de variables à gauche du signe égal soit le même que le nombre d’éléments dans la collection de droite.

D’ailleurs, ça marche même avec un seul élément :

>>> ducks = ('riri',)
>>> duck1, = ducks # notez la virgule
>>> duck1
'riri'

Et ça marche avec n’importe quel itérable, pas uniquement les tuples. Avec une liste, une string, un générateur…

>>> a, b, c, d = [1, 2, 3, 4]
>>> c
3
>>> a, b = "12"
>>> b
'2'
>>> def yolo():
    yield "leroy"
    yield "jenkins"
...
>>> nom, prenom = yolo()
>>> nom
'leroy'
>>> prenom
'jenkins'

Ça marche bien entendu avec un dico ou un set, mais comme ils ne sont pas ordonnés, c’est pas très utile.

Astuces autour de l’unpacking

On peut utiliser l’unpacking dans des endroits inattendus. Par exemple, pour échanger la valeur de deux variables :

>>> a = 1
>>> b = 2
>>> a, b = (b, a)
>>> a
2
>>> a, b = b, a # les parenthèses sont facultatives dans les tuples
>>> b
2

Puisqu’on est dans les tuples sans parenthèses, on peut retourner un tuple et donner l’illusion de retourner plusieurs variables :

>>> def duckmebaby():
...     return "rifi", 'filou', 'louri'
...
>>> et, hop, la = duckmebaby()
>>> et
'rifi'
>>> hop
'filou'
>>> la
'louri'

Allons plus loin.

On peut utiliser l’unpacking à l’intérieur d’une boucle for. Souvenez vous que les itérables peuvent contenir d’autres itérables. Par exemple, j’ai une liste qui contient 3 tuples, chaque tuple contient deux éléments :

>>> scores = [('Monique', '3'), ('David', 10), ('Dick', 1)]
>>> for score in scores:
...     print(score)
...
('Monique', '3')
('David', 10)
('Dick', 1)

Si je veux afficher le nom et le score l’un en dessous de l’autre :

>>> for nom_et_score in scores:
...     print(nom_et_score[0])
...     print(nom_et_score[1])
...
Monique
3
David
10
Dick
1

Je peux appliquer l’unpacking dans la boucle pour rendre cette opération plus élégante :

>>> for nom, score in scores:
...     print(nom)
...     print(score)
...
Monique
3
David
10
Dick
1

Cela marche avec des itérables plus gros, bien entendu. C’est aussi particulièrement utile avec des dictionnaires car on peut les transformer en itérable de tuples :

>>> scores = {'Monique': '3', 'David': 10, 'Dick': 1}
>>> scores['Monique']
'3'
>>> scores.items() # transformation !
dict_items([('Monique', '3'), ('David', 10), ('Dick', 1)])
>>> for nom, score in scores.items():
...     print(nom)
...     print(score)
...
Monique
3
David
10
Dick
1

Tout aussi utile, mais plus compliqué, est l’usage de l’unpacking dans l’appel de fonction. Pour cela, on utilise l’opérateur splat, l’étoile en Python.

Soit une fonction qui additionne des nombres :

>> def add(a, b, c):
...     return a + b + c
...
>>> add(1, 2, 3)
6

Oui, imaginons que je suis complètement débile, et que j’ai cette fonction pérave dans mon code. Vous noterez dans les articles que je l’utilise souvent sur le blog. C’est la fonction fourre tout pour expliquer un truc quand j’ai pas d’idée.

Maintenant, imaginez que je veuille additionner des canards. Si, ça marche en Python :

>>> 'riri' + 'fifi' + 'loulou' # what the duck ?
'rirififiloulou'

Maintenant je me refais mon tuples de canards :

>>> # nous entrerons dans la bande à picsou, youhou
>>> duckyou = ('riri', 'fifi', 'loulou')

Si je veux utiliser ma fonction pourrie pour mon use case stupide, je ferai ceci :

>>> add(duckyou[0], duckyou[1], duckyou[2])
'rirififiloulou'

Voilà une perte de productivité intolérable, c’est pas comme ça qu’on va faire fructifier son sou fétiche.

On peut forcer l’unpacking avec l’étoile :

>>> add(*duckyou)
'rirififiloulou'

Si on oublie l’étoile, le premier paramètre reçoit tout le tuple, et les autres paramètres rien :

>>> add(duckyou)
Traceback (most recent call last):
  File "", line 1, in 
    add(1)
TypeError: add() missing 2 required positional arguments: 'b' and 'c'

Les fonctions ont même le droit à un bonus car on peut unpacker des dictionnaires en utilisant la double étoile. Ca ne marche qu’avec les fonctions, et ça va déballer le dico pour que chaque paire clé/valeur soit passée comme nom et valeur de l’argument :

>>> def pas_add(arg1, arg2):
    print(arg1)
    print(arg2)
...
>>> pas_add(arg1="Je suis la valeur 1", arg2="Je m'en branle de qui tu es")
Je suis la valeur 1
Je m'en branle de qui tu es
>>> dicocorico = {'arg1': 'cotcot', 'arg2': 'ouai je pête un cable, l\'avion me soule'}
>>> pas_add(**dicocorico)
cotcot
ouai je pête un cable, l'avion me soule

Quand on unpacke des paramètres, il faut s’assurer que le nombre d’arguments passé n’est pas supérieur à ceux existant, sinon ça plante :

>>> dicocorico = {'arg1': 'cocot', 'arg2': 'ouai je pête un cable, l\'avion me soule', 'dang': 'je suis en trop et ça fait chier tout le monde'}
>>> pas_add(**dicocorico)
Traceback (most recent call last):
  File "", line 1, in 
    pas_add(**dicocorico)
TypeError: pas_add() got an unexpected keyword argument 'dang'
>>> stuplet = (1, 2, 3)
>>> pas_add(*stuplet)
Traceback (most recent call last):
  File "", line 1, in 
    pas_add(*stuplet)
TypeError: pas_add() takes 2 positional arguments but 3 were given

Par contre, rien ne vous empêche de fournir moins d’arguments et de remplir les autres à la main :

>>> def encore_add(a, b, c, d):
    return a + b + 0 + c + d # je feinte
...
>>> encore_add(10, *stuplet)
16

Et on peut bien entendu faire le mega mix. Par exemple, prenons la fonction print, dont la signature accepte une infinité d’arguments positionnels et quelques arguments nommés :

print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

Aller, on va lui unpacker sa mère :

>>> ducks = ['riri', 'fifi', 'loulou'] # is this duck typing ?
>>> keywords = {'sep': ' / ', "end": " : vous êtes du coin ? \n"}
>>> print('picsou', *ducks, **keywords)
picsou / riri / fifi / loulou : vous êtes du coin ?

Ça c’est fait.

Python 3, c’est du chocolat

En Python 3, l’unpacking a été amélioré, et on peut maintenant faire de l’unpacking partiel :

>>> # exemple 100% repompé d'un autre article du blog. Duck it.
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> a, *b = l
>>> a
0
>>> b
[1, 2, 3, 4]
>>> a, *b, c = l
>>> a
0
>>> b
[1, 2, 3]
>>> c
4

Ce qui peut être très pratique sur les longs itérables. Comment obtenir la dernière ligne d’un fichier ?

>>> *contenu, dernire_ligne = open('/etc/fstab')
>>> dernire_ligne
'UUID=0e8c3132-8fa2-46d5-a541-2890db9b371f none            swap    sw              0       0\n'

Ou alors, dans une boucle :

>>> for initiale, *reste in ducks:
    print(initiale)
...
r
f
l
Error happened! 0 - count(): Argument #1 ($value) must be of type Countable|array, null given In: /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php:428 http://ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/?81 #0 /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php(999): VroumVroum_Blog->update() #1 /var/www/ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/index.php(1): require_once('...') #2 {main}