Pourquoi ','.join en Python ?


PHP:

implode(" ",  array("Fulguro", "Poing"))

Perl:

join(" ", qw(Astero Hache))

Ruby:

%w(Planitron Cornofulgur).join(' ')

Et…

Python:

' '.join(['pouet', 'pouet'])
Rage comic : "Python, Y NO MAKE SENSE"

C'est un des rares rage comics qui me fasse marrer

Mais pourquoi, mon Dieu, pourquoi ?

A cause du duck typing: on se branle du type et de sa capacité à joiner, on est juste intéressé par le fait qu’il soit itérable.

Ainsi, join() accepte n’importe quel itérable:

>>> '-'.join('azerty') # une string
'a-z-e-r-t-y'
>>> '-'.join(['a', 'b', 'c']) # une liste/un dico/un tuple/un set
'a-b-c'
>>> '-'.join((str(x ** x) for x in range(10))) # un expression génératrice
'1-1-4-27-256-3125-46656-823543-16777216-387420489'
>>> '-'.join(open('/etc/fstab'))[:100] # un fichier (et tout file like object)
"# /etc/fstab: static file system information.\n-#\n-# Use 'blkid -o value -s UUID' to print the univer"
>>> def generator(l): # et n'importe quel générateur custo
    count = 1
    for x in l:
        yield x * count
        count = count * (count + 1)
...         
>>> '-'.join(generator('pouet'))[:100]
'p-oo-uuuuuu-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-ttttttttttttttttttttttttttttttttttttttttttttt'
>>> class LaClasse(object): # VRAIMENT n'importe quel itérable
    def __iter__(self):
        import datetime
        for x in xrange(10):
            yield datetime.datetime.now().strftime('%f')
...             
>>> '-'.join(LaClasse())
'902564-902628-902641-902650-902659-902667-902674-902682-902692-902710'
>>> # et même une chaîne batarde d'itérables improbables :-)
>>> list(iter(chain(LaClasse(), (str(x) for x in xrange(10))).next, '5'))
['043498', '043546', '043557', '043567', '043575', '043584', '043592', '043600', '043608', '043616', '0', '1', '2', '3', '4']

Et accessoirement ça permet de faire des trucs comme ça:

>>> join_coma = ",".join
>>> join_dash = "-".join
>>> join_coma('pouet')
'p,o,u,e,t'
>>> join_dash('pouet')
'p-o-u-e-t'

8 thoughts on “Pourquoi ','.join en Python ?

  • Romain

    ça fait un peu réfléchir à l’envers mais on s’y fait… Et encore c’est parce que les codeurs pensent de travers :

    ‘,’.join(ma_liste) => ‘,’ joint les éléments de ma liste

    Ce que propose Ruby roxe quand même pour ce qui est de la lisibilité, surtout associé à des blocs.

    Mais ‘implode’ en PHP ? pourquoi ?

  • shenshei

    et pourquoi on a:
    filter(function, sequence) et map(function, sequence)
    et pas:
    sequence.filter(function) et sequence.map(function)

    car lors de l’imbrication de plusieurs map et filter la seconde solution est nettement plus lisible (pas besoin de jouer au poupées russes)

  • Sam Post author

    Pour la même raison: pour qu’on puisse passer n’importe quel itérable à map et filter. On peut faire un map/filter sur autre chose qu’un séquence. Un itérable doit juste remplir une seule condition: pouvoir récupérer des valeurs une à une.

    Si on suit la logique sequence.filter, sequence.map, sequence.join, alors on se retrouve à demander à tous les itérable d’implémenter un proxy de filter, map et join, ce qui revient à complexifier l’interface de chaque nouvel itérable codé.

    Python fonctionne en duck typing, si c’est itérable, on peut maper, on peut filtrer, on peut joiner. L’action de maper, filtrer, joiner est indépendance de l’itérable, et donc ne fait pas partie de son interface.

  • LeMeteore

    Pourquoi map(func, iterable), merci pour l’explication. Je comprends mieux maintenant.

    J’ai un ami qui il y a longtemps m’avait expliqué que c’etait (entre autre?) pour encourager un style de programmation purement fonctionnel.

  • Romain

    OK implode est l’inverse d’explode mais PHP aurait pu coller à Perl et au reste du monde et utiliser split / join !

    Pour les histoires de map / filter / reduce : c’est vrai que des fois, on a l’impression que Python ne sait pas trop choisir entre fonction et méthodes… Dans ce cadre là, c’est le choix de rendre l’objet iterable grâce à la méthode _iter_ et pas en rajoutant une tonne de méthodes comme avec un mixin comme dans Ruby (parce que Ruby ne rigole pas avec la taille des interfaces, c’est bien chiant d’ailleurs)

  • Sam Post author

    Voilà. Moi autant ','.join me parle complètement, autant len(obj) à la place de obj.length je trouve ça bidon puisque len() ne fait qu’appeler __len__ de toute façon. Encore, si len() pouvait fallback sur __iter__, mais même pas.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.