Forum Programmation.python de la puissance de (beep) pour trouver un truc simple en 2 minutes

Posté par  (site web personnel) .
Étiquettes : aucune
0
8
juil.
2004
Je viens d'écrire un one-liner en Perl pour trouver la solution au problème suivant : lorsqu'un projet Java utilise ant pour le build, si la déclaration du package ne correspond pas au répertoire dans lequel est le fichier source, à chaque invocation de ant ce fichier sera recompilé ; mais comme ant ne montre pas ce qu'il compile avec détail, on ne sait pas quel fichier source est le fautif.

Et hop :

for i in `find -name *.java`; do
    perl -MMDK::Common -e 'my $f = $ARGV[0]; my ($pkg) = cat_($f) =~ /package (\S+);/; $f =~ s|^\./(.*)/\w+\.java|$1|; print "Fautif : $ARGV[0]" if length($f) != length($pkg)' $i;
done

Si je décompose chaque ligne :

my $f = $ARGV[0];
#- obtient le nom du package
my ($pkg) = cat_($f) =~ /^\s*package (\S+);/;
#- enlève du chemin de recherche le nom du fichier et le ./ du début
$f =~ s|^\./(.*)/\w+\.java|$1|;
#- on doit obtenir deux chaînes de même longueur (l'une avec des . et l'autre avec des / pour séparer chaque niveau de répertoire mais on s'en fout) si le package est correct
print "---> $ARGV[0]" if length($f) != length($pkg);


Donc, bon, voilà, c'est pour troller un peu : je m'émerveille que cette merveille de Perl m'ait permis de faire cela en 2 ou 3 minutes, et j'aimerais que la floppée d'adorateurs de Python me montrent comment ils l'auraient fait avec leur langage : je pense que ce sera bien plus long et plus chiant en Python, mais prouvez-moi que je me trompe !
  • # one-liner

    Posté par  (site web personnel) . Évalué à 6.

    C'est un one-liner de 5 lignes ?
  • # perso...

    Posté par  . Évalué à 2.

    Je connais peu perl mais ça m'a jamais vraiment manqué. Ce genre de petites bidouilles du quotidien, je m'en dépatouille en général en bash/sed/awk/etc. Genre là je pense que j'aurai fais un truc comme ça (tapé en plusieurs lignes parceque moi je trouve ça plus lisible - et puis soyez indulgents, j'ai pas testé):
    % for i in $(find . -name "*.java") ; do
    > 	foo=$(sed 's:\./::;s:/\w*\.java::' <<<$i)
    > 	bar=$(sed -n "s:package \(.*\);:\1:p" $i)
    > 	[[ ${#bar} -ne ${#foo} ]] && echo "BAD: $i ($bar)"
    > done
    
    À part ça, pour ce qui est de python, je trouve effectivement pas qu'il soit adapté à ce genre de petites choses, mais c'est pas vraiment le but non plus amha. C'est plus un langage pour faire des vrais programmes.
  • # Pitoyable

    Posté par  . Évalué à 4.

    Plus ça va, plus je suis exaspéré par ce genre d'attitude.

    Visiblement, on n'a pas les critères de développement en équipe.

    Tu es dans le cadre d'un travail d'équipe ("projet Java utilisant ant pour le build"), et qu'est-ce qui compte le plus pour toi ?

    - Taper le moins de lignes possibles
    - Répondre à un besoin en tapant le moins de caractères possible
    - Pondre ton code en 2 minutes chrono

    Je vais te montrer, comment j'aurais fait, en privilégiant la lisibilité et la maintenabilité.

    Voilà un code python qui répond au même besoin que ta chose illisible:
    • [^] # Exemple en python

      Posté par  . Évalué à 4.

      #!/usr/bin/env python
      
      import re
      import sys   
      package = re.compile("^\s*package (\S+)\s*;")
      
      class JavaFile:
      
          def __init__(self,location):
              self.location = location
              
          def dirName(self):
              """
              Retourne le nom du package en se basant sur
              le nom du fichier.
              Note: le nom du fichier doit etre de la forme
                  ./repertoires/associes/au/paquetage/fichier.java
              """
              return '.'.join(self.location.split('/')[1:-1])
              
          def packageName(self):
              """
              Retourne le nom du package specifie dans le code source
              """
              f=open(self.location,'r')
              lines=f.readlines()
              f.close()
              for line in lines:
                  if package.match(line):
                      return package.match(line).group(1)
              
          def isValid(self):
              """
              Retourne vrai ou faux selon que le nom
              du package specifie dans le code source
              corresponde a l'emplacement du fichier
              """
              return len(self.dirName())==len(self.packageName())
              
              
              
      if __name__=="__main__":    
          javaFile = JavaFile(sys.argv[1])    
          if not javaFile.isValid():
              print "%s is not valid." % sys.argv[1]
      
    • [^] # Re: Pitoyable

      Posté par  . Évalué à 2.

      Alors, tu peux toujours t'amuser à compter les lignes
      et les caractères des 2 codes si ça t'amuse. C'est bien, vieux, tu as déjà gagné, tu es le plus fort, ouais !!

      Moi je vais te dire ce qui m'importe:

      1. Un minimum de commentaires qui documentent le code.

      C'est sûr qu'un super pro comme toi a pas le temps de documenter du code, surtout qu'inclure de la doc dans un one-liner ça fait tout de suite ridicule, pas vrai ?

      Et puis une fois que tu as changé de job, c'est tellement marrant pour tes anciens collègues de passer 2 plombes à déchiffrer ta "merveille" imbitable de 1 ligne x 12340 caractères, faudrait quand même pas leur gâcher le plaisir, hein.


      2. Lisibilité

      Tu vois c'est con, je voudrais écrire du code imbitable en python que je pourrais même pas, puisque je suis obligé de taper une instruction par ligne.

      De mon point de vue, c'est un réel avantage pour les collègues qui reprendront le script plus tard ; de ton côté j'imagine que tu as pas que ça à foutre de séparer
      tes instructions, il faut que tu finisses de taper ton code dans les 2-3 minutes imparties parce qu'un pro comme toi a tellement d'autres truc plus importants à faire, c'est vrai quoi.

      3. Modularité, réutilisabilité et tests unitaires

      Ah la la, faire de la programmation orientée objet pour un programme aussi court, c'est n'importe quoi...

      Sauf que, outre rendre le code plus lisible, il devient également réutilisable, y compris pour des tests unitaires

      Ben oui, moi qui pensais (toi aussi visiblement) qu'un demi-dieu comme toi ne pouvait pas faire d'erreur, j'ai repris ton expression rationnelle pour récupérer le nom du package dans le code source.

      Mais j'ai aussi testé unitairement

      javaFile.packageName()

      avec un ensemble de fichiers java. Et malheureusement, des déclarations parfaitement valides comme celle-ci:

      package a.b.c ;

      comportant un ou plusieurs espaces avant le point-virgule, ne passent pas avec ton expression rationnelle. C'est donc une erreur manifeste de ta part, et je plains les mecs qui reprendront tes erreurs dans tes one-liners de la mort après ton départ.

      Donc pour information, l'expression rationnelle correcte est

      "^\s*package (\S+)\s*;"


      Voilà. Bilan de l'opération:

      - ton code est illisible
      - ton code est non-documenté
      - ton code est erroné

      mais tu l'as vite fait, mal fait, et en utilisant ton langage favori. Bravo champion.

      Finalement, je suis bien content de ne pas bosser avec toi, parce que les conséquences d'une telle attitude sont très néfastes.

      Et ça me fait d'autant moins rigoler quand je vois le code des outils de la Mandrake, auxquels tu as contribué : code imbitable, quasi-absence de documentation, galère à maintenir (comme ton one-liner, quoi). Malheureusement c'est à mon humble avis la plus grosse faiblesse de la distribution Mandrake linux, et pour longtemps encore.

      Alors franchement, pas de quoi s'émerveiller, j'aurais plutôt honte à ta place. Change vite de méthode de développement, eut égard à tes collègues... et à tes utilisateurs.
      • [^] # Re: Pitoyable

        Posté par  (site web personnel) . Évalué à 2.

        (je passe sur la duplication de commentaires par rapport à ton premier, auquel j'ai déjà répondu plus bas, au sujet de mon code caca pas documenté toussa) (toi d'ailleurs qui a l'air de mettre en haute estime la qualité, lisibilité et maintenabilité du code, je te rappelle que tu dois l'éviter dans ton code, la duplication, mais passons)

        D'ailleurs je me demande pourquoi y'a une agression par ligne, j'ai dû te contredire à un moment où tu n'étais pas d'humeur ?

        Tu vois c'est con, je voudrais écrire du code imbitable en python que je pourrais même pas, puisque je suis obligé de taper une instruction par ligne.

        Ben, non.

        >>> print "foo"; print "bar"
        foo
        bar
        >>>

        comportant un ou plusieurs espaces avant le point-virgule, ne passent pas avec ton expression rationnelle.

        Ben ouais j'ai fait une erreur sur cette regex, et alors ? Tu ne cesses de parler de demi-dieu et compagnie mais je te signale au passage que je n'ai jamais affirmé quoi que ce soit sur la perfection de mon code, ici comme ailleurs. Si tu fais toujours du code parfait sans erreur, bravo. Tu dois être le seul je pense. Et pense à ranger un peu le venin et à revenir sur Terre...


        Et pour finir tu continues l'agressions, sur Mandrake et son code.. bravo..
        Franchement à part te répondre "va te faire voir pauvre con" là j'ai plus d'idée. Tu juges sans connaître, tu racontes n'importe quoi, tu déverses ton aggressivité de manière totalement gratuite... Non franchement j'ai plus d'autre idée :/...
        • [^] # Re: Pitoyable

          Posté par  . Évalué à 2.

          *Sincèrement* désolé pour l'agressivité.

          Je suis entouré de gens qui proposent comme solutions du code illisible, torché en 2 minutes, et dont je sais pertinemment qu'ils poseront plus de problème qu'ils n'en résoudront sur le long terme.

          Dans le même temps, je suis horrifié par le code des outils de la Mandrake, *objectivement* très peu documenté et lisible. Ça me fait sincèrement chier de voir l'effort nécessaire pour les maintenir, à côté des outils de la Fedora, certes pas aussi avancés mais limpides à parcourir, et en plein boom.

          Et sur ce je vois ton journal, dont le ton est, sinon agressif, très provocateur : une merveille d'un côté, quelque chose de forcément bien plus long et plus chiant en Python de l'autre... avoue que tu y es pas allé de main morte !!

          Alors j'ai répondu à ta provocation, d'autant plus que je suis absolument persuadé que ce que tu prônes, c'est pour moi un exemple à ne pas suivre. Quelle que soit la finalité du code, d'ailleurs, ça ne rentre pas en ligne du compte dès l'instant où ça s'intègre au sein d'un travail d'équipe.

          Donc enterrons provocation et agressivité... et dis-moi ce que tu penses de l'équivalent en python... tu n'es pas tenté ?
          • [^] # Re: Pitoyable

            Posté par  (site web personnel) . Évalué à 3.

            Dans le même temps, je suis horrifié par le code des outils de la Mandrake, *objectivement* très peu documenté et lisible. Ça me fait sincèrement chier de voir l'effort nécessaire pour les maintenir, à côté des outils de la Fedora, certes pas aussi avancés mais limpides à parcourir, et en plein boom.

            Pour Fedora, on verra ce que ça donnera, mais jusqu'à présent les outils d'aide à la configuration de Mandrake sont plus nombreux et plus puissants alors que les développeurs sont moins nombreux (ils sont un peu plus buggés je le concède, mais globalement ils marchent très bien).

            Maintenant, la question de la documentation du code ne se résume pas à un principe qui tient en deux lignes. En particulier, il faut tenir compte du niveau du/des programmeurs, du taux de "turnover" dans l'équipe de développement (Mandrake c'est pas une SSII), du style qu'adopte une équipe de développement (en connaissant le style on comprendra mieux le code), du besoin de maintenance et d'ajout de fonctionnalité du code (il n'est pas le même dans chaque code/projet : par exemple ce que tu documenteras le mieux sera l'API, les exemples de plugin). Bref, mon point de vue sur la question s'étale en anglais ici :

            http://zarb.org/~gc/html/documenting-code.html(...)

            Et sur ce je vois ton journal, dont le ton est, sinon agressif, très provocateur : une merveille d'un côté, quelque chose de forcément bien plus long et plus chiant en Python de l'autre... avoue que tu y es pas allé de main morte !!

            Euh ce n'est pas parce que mon journal est provocateur (mais pas insultant à part peut-être "chiant" mais bon faut pas pousser) que j'ai le droit de me prendre : "t'as gagné t'es le meilleur" "un super pro comme toi" (plusieurs fois) "trucs plus importants à faire" "un demi-dieu comme toi" "bravo champion" bon j'arrête mais bon tu situes : au premier ça va je rigole, au deuxième je me dis que t'as l'air chaud, au cinquième j'en ai franchement marre.

            je suis absolument persuadé que ce que tu prônes, c'est pour moi un exemple à ne pas suivre.

            Je ne prône que l'utilisation d'un langage de script élégant et puissant pour les "petites tâches vite fait", rien de plus, dans cet article tout du moins. Je sais que le code ci-dessus est illisible, mais son propos n'est pas d'être relu et encore moins d'être conservé ; c'est juste d'avoir un outil permettant de faire rapidement une tâche unique.

            dis-moi ce que tu penses de l'équivalent en python... tu n'es pas tenté ?

            Non. Python pour moi a des avantages indéniables, mais ils sont massacrés par trop d'erreurs. Je te liste les plus importantes que j'ai découvertes lors de mes essais (parfois forcés) avec Python :

            fonctions anonymes castrées

            On se trouve relativement souvent en face d'une situation dans laquelle on veut appliquer une transformation assez simple à un ensemble de données, et les fonctions anonymes sont indispensables pour pouvoir le faire élégamment (elles le sont aussi entres autres pour brancher un callback dans un système de programmation évenementielle).

            Par exemple, j'ai une liste d'entiers, je veux obtenir à la fois la somme de ceux-ci, et la liste avec les entiers doublés. En Perl on le fait comme cela :

            @nouvelle_liste = map { $somme += $_; 2*$_ } @liste

            Ca pourra te paraître imbitable si tu ne connais pas le Perl et sa variable implicite $_, mais une fois que l'on connaît Perl ça devient très lisible. C'est comme si on disait que l'indentation horizontale de Python rend le code imbitable parce qu'on est habitués aux accolades - c'est juste une particularité du langage, qui une fois connue n'est pas un obstacle à la lisibilité.

            En Ruby on le fait comme cela :

            somme = 0
            nouvelle_liste = list.map{ |e| somme += e; 2*e }

            C'est très élégant aussi, on peut discuter entre $_ et |e| qui oblige à nommer la variable locale mais ça n'alourdit quasimment pas.

            En Python on doit définir une fonction, ou bien utiliser un for... in..., mais on ne pourra plus utiliser d'autre appel fonctionnel derrière, ce qui est génant ; si on veut vraiment le faire fonctionnellement on devra écrire :

            somme = 0
            nouvelle_liste = map(lambda e:globals().update({'somme':globals().get('somme') + e}) or 2 * e, liste

            Pour moi c'est un énorme problème de Python, qui m'empêche d'écrire du code fonctionnel. Et au passage, en terme de qualité, réutilisabilité et maintenabilité, le code fonctionnel est d'une utilité fantastique car il force à écrire des fonctions sans effets de bords.

            Sur ce point-là, je suis d'accord que ce n'est pas ce qu'un programmeur débutant fera, mais par contre au bout d'une certaine expérience de programmation le fonctionnel s'impose ; ce qui alimentera pour partie ma future conclusion sur Python.

            message d'erreur totalement cryptique

            Si tu écris cela :

            class Foo:
                def __init__(self):
                    print "Foo"

            class Bar(Foo):
                def __init__(self):
                    super(Bar, self).__init__()
                    print "Bar"

            b = Bar()

            Tu te prends comme message d'erreur :

            File "foo.py", line 7, in __init__
            super(Bar, self).__init__()
            TypeError: super() argument 1 must be type, not classobj

            Si ça t'intéresse, je te laisse tenter de comprendre le sens du message et de trouver d'où vient ce problème, je donne la solution plus bas au [1].

            Bien sûr, ce n'est qu'un exemple et ça ne veut pas forcément parler d'un langage en général. Mais franchement, j'ai burlingué auprès de quelques langages maintenant, et je n'ai pas souvenir d'avoir eu avec d'autres langages que Python un message aussi débile (à part peut-être les messages de g++ quand il y a un problème emmêlant les template, les namespace... et heureusement qu'il n'y a pas de multiple dispatch en plus).

            interpolations

            Il n'y a pas d'interpolation dans les chaînes de caractères. Quelque chose de si pratique disponible dans tellement de langages de script (même Tcl !) et qui n'existe pas en Python, je trouve ça vraiment très dommage. On pourra me dire que ça rend le programme plus lisible, mais au contraire, devoir passer "de droite à gauche" comme on le fait dans un format en C ne rend que le programme moins lisible. C'est un détail mais c'est prodigieusement agaçant quand on programme avec Python.

            objet ? pas objet ?

            On fait la publicité de Python comme langage objet. Mais pourquoi n'ont-ils pas fait les choses jusqu'au bout ? C'est objet mais pour avoir la longueur d'une chaîne, là où Ruby a fait le boulot correctement (chaine.length) en Python c'est len(chaine). Conversion d'une chaine vers un entier ? En Ruby je fais chaine.to_i en Python rebelote je dois faire int(chaine).

            Notons que je n'affirme pas que Perl est mieux là-dessus, Perl est une catastrophe pour l'objet (on peut quand même être heureux d'avoir l'héritage multiple en Perl, surtout quand on fait du Java par ailleurs) ; mais plutôt que si on veut un langage de script élégant, moderne, où tout est objet, il faut vraiment choisir Ruby sur Python.


            Et voilà ma conclusion sur Python, je disais que l'absence de fonctionnel l'alimenterait ; les points ci-dessus aussi. Je vais te choquer certainement si tu aimes bien Python, mais je le dis car je l'argumente : pour moi, Python est un bon langage pour mauvais programmeur. Il est parfait dans ce cas-là car il interdit beaucoup de choses élégantes et/ou puissantes car trop compliquées et "imbitable/illisible". Je suis d'accord qu'avec Python il est facile de relire le programme d'un autre, du coup. Mais par contre quand je programme en Python, je m'arrache les cheveux en ayant l'impression d'être castré comme chez Java. Il est bon pour les mauvais programmeurs car tu es sûr qu'ils ne te feront pas quelque chose d'aussi dégueulasse que si ils avaient programmé en Perl ou en C++. Mais je trouve, en tant que programmeur expérimenté (ça sonne prétentieux je sais mais bon ça fait un certain temps que j'en fais, je connais quelques trucs, il m'en reste plein à apprendre et je rencontre souvent des gens qui m'apprennent des choses insoupçonnées, mais je suis expérimenté quand même na), qu'utiliser Python restreint trop l'expressivité de mes programmes et par là-même la vitesse de développement, sans apporter de vrai bénéfice de maintenabilité (on peut faire aussi maintenable en Perl et en Ruby en suivant des consignes cohérentes de style de programmation, qui sont de toutes façons indispensables avec n'importe quel langage quand on développen en équipe).

            En deux mots, Python suxor :).


            [1] solution : remplacer "class Foo:" par "class Foo(object):" ; j'ai eu de la peine à comprendre le rapport entre le message d'erreur et ça (le "super argument 1" c'est Bar, pas Foo, que je sache !?) et encore moins la différence entre les deux définitions de classe, je ne vois pas trop ce qu'une "classe" peut être d'autre qu'un objet et donc je pensais sincèrement qu'elle héritait implicitement de la top-class object, mais bon apparemment non ; d'ailleurs dans ton code plus haut tu n'hérites pas d'objet, attention à ne pas te heurter un jour à ce problème
            • [^] # Python et formes fonctionnelles

              Posté par  . Évalué à 2.

              fonctions anonymes castrées

              ;-)
              les pauvres !

              Je vais pas avoir le temps de répondre à tout aujourd'hui...
              allons-y toujours bien pour les formes fonctionnelles : je ne discuterai pas de leur intérêt, tu prêches un convaincu.

              Mais justement, tu as dû passer à côté en python, parce que tout ce que tu peux
              faire en perl, tu peux le faire en python !

              L'équivalent de:

              @nouvelle_liste = map { $somme += $_; 2*$_ } @liste;

              en python est:

              nouvelle_liste = map(lambda x:2*x,liste)
              somme = sum(nouvelle_liste)

              Ca pourra te paraître imbitable si tu ne connais pas le Perl et sa variable implicite $_

              ben justement, j'étais fervent partisan de perl... avant de me convertir à python (ce qui n'a pas été immédiat).

              Donc, concernant les lambda-forms, propose-moi une forme en perl, et je te donne l'équivalent en python.

              Mais jusqu'à preuve du contraire, ça te fait un argument de moins.
              • [^] # Re: Python et formes fonctionnelles

                Posté par  (site web personnel) . Évalué à 2.

                Le problème c'est le fait que le lambda n'est pas capable de capturer l'environnement (l'extérieur), on ne peut donc pas créer de closure[1] et c'est pour cela que ces fonctions anonymes sont castrées. Dans mon exemple effectivement c'est peu gênant, et tu t'en sors bien :) mais dans de nombreuses situations c'est la merde : par exemple quand tu veux brancher un callback, dans le callback typiquement tu voudras modifier la valeur d'une variable à l'extérieur du lambda, en Python tu es obligé d'utiliser globals().get() et globals().update() comme j'ai montré, ce qui donne un lambda très lourd. Sans compter que dans le cas d'une variable locale à la méthode dans laquelle le lambda est exécuté, a priori on ne peut tout simplement pas y accéder.

                [1] http://foldoc.doc.ic.ac.uk/foldoc/foldoc.cgi?query=closure(...)
                • [^] # Re: Python et formes fonctionnelles

                  Posté par  . Évalué à 2.

                  Si, si, les fermetures sont bien sûr possibles en Python:
                  def maFonction(arg):
                      pass
                  
                  # On applique maFonction à dous les élément de la liste
                  map(lambda x,f=maFonction:f(x), liste)
                  
                  Évidemment sans ça tu devais être emmerdé. Mais vas-y, exprime tes besoins en perl, et je te les transcris en python !
                  • [^] # Re: Python et formes fonctionnelles

                    Posté par  (site web personnel) . Évalué à 2.

                    Rah mais tu vois bien que c'est pas une fonction anonyme nomdidjiou ! Tu es obligé de la nommer, la déclarer, les fonctions sont des first-class value comme en C ou en Java heureusement on n'est pas en Pascal quand même ; mais tu ne peux pas spécifier ta fonction anonymement dans le map.
                    • [^] # Re: Python et formes fonctionnelles

                      Posté par  . Évalué à 2.

                      Relis l'exemple: maFonction est simplement un exemple d'un objet global auquel j'accède dans ma fonction anonyme.

                      Tout ça pour répondre au besoin que tu exprimais (pour ne plus te froisser, parce que ça ressemblait plutôt à une affirmation fausse):

                      dans le callback typiquement tu voudras modifier la valeur d'une variable à l'extérieur du lambda, en Python tu es obligé d'utiliser globals().get() et globals().update() comme j'ai montré

                      Tu vois bien que maFonction qui est globale, est accédée sans faire appel à global(). Et la fonction anonyme n'est pas nommée. Je recommence:

                      map(lambda fonctionAnonyme:x,g=objetGlobalQuelconque : <corps de la fonction anonyme>, liste)


                      Et encore une fois, si tu penses qu'on ne se comprend pas, prends un exemple concret en perl, que je te le transcrive en python.
                      • [^] # Re: Python et formes fonctionnelles

                        Posté par  (site web personnel) . Évalué à 2.

                        Oui, la façon d'accéder à la valeur de la variable au moment de la création du lambda est de le nommer comme paramètre du lambda. Mais tu ne peux pas changer cette variable, car tu n'en as pas la "référence" mais bien la valeur. Puisque tu me demandes de te proposer d'écrire du code : reprends juste mon exemple avec le calcul de la somme dans le lambda. Essaie de nommer "somme" comme paramètre du lambda, et de lui additioner x, avant de rendre x*2 comme résultat du lambda. Tu ne pourras pas le faire, à ma connaissance.

                        Je réitère : ce n'est certes pas une closure, avec capture de la variable, mais bien une capture de la valeur au moment de la création du lambda ce qui n'est pas la même chose, et en tous cas bien moins intéressant :

                        liste = [ 1, 2, 3 ]
                        somme = 5
                        l = lambda x, somme=somme : somme
                        print map(l, liste)
                        somme = 7
                        print map(l, liste)

                        donnera :

                        [5, 5, 5]
                        [5, 5, 5]

                        alors qu'une closure aurait pu permettre d'obtenir la nouvelle valeur de somme dans le deuxième appel
                        • [^] # Bof...

                          Posté par  . Évalué à 2.

                          Si vraiment y'a que ça qui te bloque...

                          liste = [ 1, 2, 3 ]
                          def s1(): return 5
                          def s2(): return 7
                          somme = s1
                          l = lambda x, s=somme : s()
                          print map(l, liste)
                          somme = s2
                          print map(l, liste)

                          donne:

                          [5, 5, 5]
                          [7, 7, 7]

                          Tu sais pertinemment à quel moment se fait l'évaluation en python. Fais un déréférencement quand tu en as besoin, point barre. Chaque langage fait son choix du traitement des valeurs et des références, rien de nouveau sous le soleil.

                          Autre exemple ??
                          • [^] # Re: Bof...

                            Posté par  (site web personnel) . Évalué à 2.

                            Euh ton code me donne deux fois [5, 5, 5] (Python 2.3.4) - puisque le lambda capture encore une fois la valeur de la fonction à sa création (s1 donc) et ton affectation de s2 à somme n'y change donc rien (tu as testé ton code ?). Mais ça me semble une bonne idée que je vais continuer :

                            liste = [ 1, 2, 3 ]
                            somme = 5
                            def s1(): return somme
                            l = lambda x, s=s1 : s()
                            print map(l, liste)
                            somme = 7
                            print map(l, liste)

                            Qui me donne bien [5, 5, 5] et [7, 7, 7], puisque la valeur capturée est la fonction s1 qui elle capture bien la variable somme en tant que telle et non sa valeur, une fois appelée elle saura rendre la vraie valeur de somme.

                            Il est donc possible de "simuler" une closure en Python en définissant un getter pour la variable que l'on veut capturer - je ne m'en étais pas douté, cette discussion aura au moins l'avantage de me révéler ça :). J'aurais supposé qu'en changeant la valeur de somme depuis la fonction s1 j'aurais même pu réaliser le fameux truc qui nous occupe (changer une variable locale/globale depuis un lambda sans avoir recours à global()) mais Python me dit "local variable 'somme' referenced before assignment". Il veut bien que j'affecte x à somme mais pas que j'ajoute x à somme, ça doit être un truc commun en Python mais là je vois pas, tu sauras sûrement pourquoi.

                            Bon, c'est déjà ça - mais si on souhaite simuler des closure comme cela en Python on ne peut pas franchement dire que ce soit d'une élégance folle, tu ne trouves pas ? On se retrouve quand même avec trois "noms" différents pour une même variable : son vrai nom, le nom de son wrapper, et son nom affecté en paramètre du lambda...
                            • [^] # Re: Bof...

                              Posté par  . Évalué à 2.

                              Euh [...] (tu as testé ton code ?)

                              Nope... damned, tu m'as eu ! Mais l'idée y était.
                              Autre façon, plus utile car plus susceptible de coller à du code réel en python il me semble:

                              class C1:
                              ....val=5

                              class C2:
                              ....val=7

                              liste = [ 1, 2, 3 ]
                              a=C1
                              l = lambda x : a.val
                              print map(l, liste)
                              a=C2
                              print map(l, liste)

                              Et celui-là je l'ai testé ;)

                              Il est donc possible de "simuler" une closure en Python en définissant un getter pour la variable que l'on veut capturer - je ne m'en étais pas douté, cette discussion aura au moins l'avantage de me révéler ça :)

                              À la bonne heure...

                              on ne peut pas franchement dire que ce soit d'une élégance folle, tu ne trouves pas ?

                              Le code ci-dessus est plus propre ; m'enfin autant les formes fonctionnelles c'est du quotidien et c'est indispensable, autant la fermeture ça m'a encore jamais manqué ; j'imagine que tu dois pas en avoir un besoin cruel tous les jours, donc pour l'occasion tu peux bien accepter d'avoir ponctuellement du code qui ait pas une forme idéale.

                              Par exemple ce qui me manque pas mal c'est l'opérateur conditionnel ternaire (condition)?xx:yy. Tant pis, je fais sans, et ça n'est certainement pas son absence qui me fera dire de python comme toi "un bon langage pour mauvais programmeur". Ça n'est pas faux, d'ailleurs, c'est juste aussi un bon langage pour bon programmeur ;)
                              • [^] # Re: Bof...

                                Posté par  (site web personnel) . Évalué à 2.

                                Bon, je me suis décidé à chercher ce "local variable reference before assignment" et j'ai trouvé que lorsqu'on affecte une variable dans une méthode, par défaut elle est locale, il faut donc spécifier "global variable" avant. Pour moi c'est encore une mauvaise surprises avec Python : la fonction qui est "return somme" accède bien à la variable globale somme, mais la fonction qui est "somme = 1" crée une nouvelle variable locale somme. Ce n'est pas logique... Si on a besoin de mettre global somme en tête d'une fonction pour manipuler la variable globale, il faut que l'on doive le faire dans les deux situations, pas une seule. Que penses-tu de ce problème ?

                                Donc, bref, j'ai écrit le bout de code que j'ai spécifié plus haut (tu me demandes de te proposer du code à écrire en Python mais tu ne l'as toujours pas écrit, tu le noteras), sans utiliser globals() grâce à ton "trick" de passer par des fonctions getter/setter :

                                liste = [ 1, 2, 3 ]
                                somme = 0
                                def getSomme():
                                    return somme
                                def setSomme(x):
                                    global somme
                                    somme = x
                                nouvelle_liste = map(lambda x, g=getSomme, s=setSomme : s(g()+x) or x*2, liste)

                                Bon, donc ok de cette manière on arrive à faire une closure, mais que d'efforts ! Et une fonction anonyme qui demande la définition de deux fonctions par variable extérieure à manipuler, hum...

                                Pour répondre à ta question sur l'utilité des closure : d'une part, quand tu l'as dans le langage que tu utilises (perl, ruby, ocaml par exemple) tu as plus tendance à l'utiliser, ça ne me semble pas choquant que tu aies l'impression de ne pas en avoir l'utilisation. Dans la même veine que le calcul de la somme ci-dessus, qui te demande en Python un deuxième parcours de la liste pour calculer la somme comme tu l'as écrit avec sum(liste), on peut en avoir besoin lorsqu'on a besoin de calculer quelque chose d'autre de non trivial (pour lequel il n'existe pas une fonction déjà définir comme sum) en même temps que le parcours de la liste, ou bien lorsque le parcours de cette liste est très coûteux, ou bien encore lorsqu'il provoque un effet de bord qui oblige à ne la parcourir qu'une seule fois. Un autre exemple courant est le branchement d'un callback sur un événement : typiquement dans gtk, par exemple avec le code suivant :

                                sub run_my_dialog() {
                                    my $results = 1;
                                    my $dialog = make_gtk_dialog();
                                    $dialog->button1()->signal_connect(clicked => sub { $results = 2 })
                                    $dialog->run();
                                    return $results;
                                }

                                La possibilité d'écrire une fonction anonyme qui soit une closure est ici particulièrement pratique pour "sauver" une valeur qui sera disponible lorsque le dialogue sera fermé. Sans closure, écrire cette fonction devient beaucoup plus lourd (tu peux l'écrire en Python ?).

                                Sinon, au sujet des expressions ternaires, je m'en mords les c*** de ne pas m'en être servi pour ma critique de Python :) c'est vrai que c'est dommage d'avoir oublié ça (comme il est dommage de ne pas avoir mis "variable++" en Python ou en Ruby d'ailleurs) même si on s'adapte aux petites erreurs de son langage favori.

                                Alors pour terminer ce thread trop long sur une note plus positiive : je suis bien sûr d'accord que chaque langage a ses défauts : en Perl, l'objet est mal branlé, les $/@/% pour les types de données sans possibilité de nester c'est chiant, les prototypes des fonctions qui n'en sont pas vraiment et la nécessité d'affecter à la main les paramètres des fonctions dans des variables locales c'est naze, et j'en passe probablement beaucoup ; en Python on a quelques soucis comme déjà énumérés ; et en Ruby il y a aussi son lot de frustration, mais on ne va pas commencer avec ça. Ce thread a beaucoup dérivé lorsque tu m'as demandé si j'étais pas "tenté" par Python, mais je voudrais repréciser mon propos initial : je voulais montrer que pour les petits one-liner, les programmes très compacts et "jetables", Python n'est pas adapté ; je pense que les fonctionnalités de Python en font un langage adapté pour les "vrais" programmes plutôt que les one-liner (ce que tu disais toi-même plus haut il me semble) (même si je ne vois que des avantages à Ruby sur Python pour faire ce genre de programmes), je voulais donc dire que pour le genre de tâche que j'exposais au début, Python ne me semble pas adapté, et même si l'on aime Python pour plein de raisons, peut-être est-ce judicieux d'utiliser quelque chose d'autre - ce que disait un des commentaires du début, même si utiliser sed quand on a perl sous la main me semble relever du masochisme :).
            • [^] # Re: Pitoyable

              Posté par  . Évalué à 2.

              Je connais un tout petit peu Python, sans vraiment l'utiliser.

              On voit qu'il y a vraiment une différence d'esprit entre Perl et Python. Un one-liner doit être quelque chose de presque inconcevable pour un programmeur Python. :) C'est marrant, c'est sûr, un petit défi technique, mais c'est pas vraiment clair.

              Plus globalement, je trouve Python ou même Ruby beaucoup plus attirants que Perl, ils sont moins complexes (toutes les variables spéciales de Perl, la syntaxe...). Et puis j'aime pas les $ (et autres) devant les noms de variables :)

              Sinon pour les quelques défauts de Python que tu décris, c'est intéressant mais certains sont des cas très spécifiques, non ? ;)

              Pour y répondre un tout petit peu, y'a quand même un "texte".__len__() (mais pas de .__int__() ;).

              Par contre j'ai pas trop compris l'intérêt qu'il y a à faire la somme et le produit par 2 d'une liste en même temps ? Je dirais la même chose que bobert.

              Et pour l'interpolation, on peut quand même éviter les formats à la C, mais c'est plus une chaîne de caractères, après je ne sais pas s'il y a des inconvénients avec ça... :
              print "J'ai", age, "ans."
              • [^] # Re: Pitoyable

                Posté par  (site web personnel) . Évalué à 2.

                Et puis j'aime pas les $ (et autres) devant les noms de variables :)

                Ouais ça ca sux. Sans compter le fait que les @tableaux et les %hash ne sont pas "nestables" en l'état, il faut utiliser des références. Ça alourdit beaucoup la compréhension et ça complexifie le langage. Ils disent qu'ils vont changer tout ça dans perl6.

                Sinon pour les quelques défauts de Python que tu décris, c'est intéressant mais certains sont des cas très spécifiques, non ? ;)

                C'est des cas dans lesquels je me suis heurté à l'utilisation, donc... je ne pense pas avoir spécialement une utilisation spécifique. Ce sont des problèmes qui m'ont beaucoup gêné, et qui n'existe pas ni en Perl ni en Ruby.

                texte".__len__()

                Ah, je ne connaissais pas. C'est mieux que rien. Mais bon si on veut critiquer la lisibilité d'un programme Perl et dire que Python c'est lisible, ce n'est pas avec des méthode avec deux _ de chaque côté qu'on va y arriver :)

                Quant au print avec multiples arguments, il est peu utilisable en l'état car il ajoute un espace entre chaque élément obligatoirement (si tu connais un moyen de ne pas avoir ça...).
                • [^] # Re: Pitoyable

                  Posté par  (site web personnel) . Évalué à 2.

                  Comme ça ?

                  >>> arg1 = "dede"
                  >>> arg2 = "toto"
                  >>> print arg1 arg2
                  >>> print arg1,arg2
                  dede toto
                  >>> print arg1+arg2
                  dedetoto
                  • [^] # Re: Pitoyable

                    Posté par  (site web personnel) . Évalué à 2.

                    Non, pas comme ça.

                    >>> a = 4
                    >>> b = "meuh"
                    >>> print b + a
                    Traceback (most recent call last):
                    File "", line 1, in ?
                    TypeError: cannot concatenate 'str' and 'int' objects
                • [^] # Re: Pitoyable

                  Posté par  . Évalué à 2.

                  Ça y est, je l'ai ! :)
                  (Non, je n'y suis pas depuis des heures. ;)

                  age = 123
                  print "J'ai " + `age` + " ans"

                  Avec la concaténation, aucun espace n'est rajouté. Finalement c'est vrai que c'est un peu plus long que ce qui se fait en perl, bash, etc., mais bon, le résultat est là. ;)
                  • [^] # Re: Pitoyable

                    Posté par  (site web personnel) . Évalué à 2.

                    ouf :)

                    c'est pas génial mais c'est vrai que c'est mieux que rien (j'en apprends sur Python en ce moment moi c'est fou) - et pour info, je fais du java toute la journée et on utilise "J'ai " + age + " ans" (grâce aux méthodes de conversion de type nommées implicitement, une bonne idée qu'on trouve dans ruby aussi avec to_s to_i etc) et je trouve déjà ça lourd et chiant à tapper...
    • [^] # Re: Pitoyable

      Posté par  (site web personnel) . Évalué à 2.

      Euh atterris c'est pas un truc qui doit faire partie du logiciel ou de ses outils, c'est un script one-time pour trouver rapidement un truc foireux au sein de plusieurs centaines de classes.

      D'ailleurs après discussion avec les autres, j'ai intégré cette vérification à l'outil de vérification statique de qualité et de standardisation de notre code. Et au passage je te signale que c'est moi qui ait proposé et programmé cet outil, donc comme type qui n'aime pas la lisibilité et la maintenabilité du code, je pense que je suis pas franchement ton client.
  • # Python et formes fonctionnelles (reloaded)

    Posté par  . Évalué à 2.

    Je reprends ici le thread plus haut qui devenait ingérable...
    
    
    Un autre exemple courant est le branchement d'un callback sur un événement : typiquement dans gtk, par exemple avec le code suivant :
     
    
    sub run_my_dialog() {
         my $results = 1;
         my $dialog = make_gtk_dialog();
         $dialog->button1()->signal_connect(clicked => sub { $results = 2 })
         $dialog->run();
         return $results;
     }
    
    La possibilité d'écrire une fonction anonyme qui soit une closure est ici particulièrement pratique pour "sauver" une valeur qui sera disponible lorsque le dialogue sera fermé. Sans closure, écrire cette fonction devient beaucoup plus lourd (tu peux l'écrire en Python ?).
    Voilà un exemple qui n'est pas une transcription littérale du code perl, mais qui je pense reste dans le même esprit ; teste-le et dis-moi ce que tu en penses:
    
    
    #!/usr/bin/env python
    
    import gtk
            
    class EntryDialog(gtk.Dialog):
        def __init__(self,message="",defaultResult=1):
            gtk.Dialog.__init__(self)
            self.result = defaultResult
            self.set_modal(gtk.TRUE)
            self.entry = gtk.Entry()
            self.entry.set_text(str(self.result))
            self.action_area.pack_start(self.entry)
            self.entry.show()
            self.entry.grab_focus()
            self.button = gtk.Button("OK")
            self.button.connect("clicked", self.click)
            self.action_area.pack_start(self.button)
            self.button.show()
            self.rButton = gtk.Button("Recommended Value")
            self.action_area.pack_start(self.rButton)
            self.rButton.show()
        def quit(self, w=None, event=None):
            self.hide()
            self.destroy()
            gtk.mainquit()
        def click(self, button):
            self.result = self.entry.get_text()
            self.quit()
        
    def runMyDialog():
        dialog = EntryDialog()
        dialog.rButton.connect('clicked',
            lambda x,d=dialog: setattr(d,'result',2) & d.quit())
        dialog.run()
        print "result=%s" % dialog.result
    
    if __name__=="__main__":
        runMyDialog()
    
    • [^] # Re: Python et formes fonctionnelles (reloaded)

      Posté par  (site web personnel) . Évalué à 2.

      Ben euh je pense que c'est long et verbeux :) tu me montres que tu ne peux pas faire de closure en python et que tu es donc obligé de définir une méthode pour ton callback, alors que le code très court se prêtait particulièrment bien à être défini par une fonction anonyme... rien de nouveau sous le soleil, et j'aurais plutôt tendance à dire que tu viens de prouver toi-même la validité de ce que je disais ?

      On pourrait d'ailleurs au passage noter la verbosité de devoir écrire "self." tout le temps, un cas qui est traité élégamment en Ruby par l'utilisation de @ pour préfixer les identificateurs, mais on ouvrirait encore un autre thread...
      • [^] # Re: Python et formes fonctionnelles (reloaded)

        Posté par  . Évalué à 2.

        Ben euh je pense que c'est long et verbeux :)

        Arrête, tu es de mauvaise foi, on repart pas sur des bonnes bases là...

        Long ? Tu compares quoi avec quoi ?
        Tu n'as écrit qu'une procédure ; j'ai écrit un programme complet !!
        Ecrire l'équivalent en perl serait *aussi* long !!

        tu me montres que tu ne peux pas faire de closure en python

        Non, je te montre que *rien* ne justifie que tu parles d' énorme problème de Python, qui t'empêche d'écrire du code fonctionnel. Tu peux me dire à la limite que python te donne des boutons, que tu le supportes pas, mais dans la pratique, je t'ai demandé des cas concrets, où tu te sentirais "bloqué", pour te montrer le code équivalent en python pour répondre au besoin.

        Reprenons:

        1. tu as commencé par affirmer que, quand tu veux écrire en perl,

        @nouvelle_liste = map { $somme += $_; 2*$_ } @liste

        en python selon toi on doit définir une fonction, ou bien utiliser un for... in.... Au départ de l'action c'était ça ton problème super bloquant.

        J'y ai répondu par le code équivalent en python:

        nouvelle_liste = map(lambda x:2*x,liste)
        somme = sum(nouvelle_liste)

        2. tu as ensuite affirmé (sans convenir que ta situation concrète n°1 n'était plus bloquante du coup) que le problème c'est le fait que le lambda n'est pas capable de capturer l'environnement (l'extérieur)

        Je t'ai montré que c'était faux:

        map(lambda fonctionAnonyme:x,g=objetGlobalQuelconque : <corps de la fonction anonyme>, liste)

        3. tu t'es ensuite arquebouté sur le manque de fermeture des formes fonctionnelles en python, que c'en était tellement bloquant que rien que d'y penser tu te sentais mal.

        De deux choses l'une:
        - soit tu en fais une question de principe ("je refuse de coder quoi que ce soit dans un langage qui n'offre pas de formes fonctionnelle avec fermeture") mais bizarrement tu as l'air de coder en java dans ta journée sans que ça te pose problème...
        - soit c'est effectivement indispensablissime, et tu as des cas concrets de code en perl mettant en oeuvre une telle fonctionnalité, et dont l'équivalent en python te semble irréalisable

        Nous en sommes donc là, sachant que dans de tels cas hypothétiques, où je bloquerais sur un équivalent en python, nous avons déjà convenu tous les deux dans l'intervalle qu'on pouvait toujours simuler la fermeture en python, et de façon plutôt plutôt élégante, cf ma deuxième proposition d'un tel code.

        Tu m'as proposé une routine perl, et j'ai mis en oeuvre son équivalent en python dans un programme complet pour que tu puisses le tester : tu as le beurre, l'argent du beurre et la crémière, petit veinard...

        En attendant, j'attends toujours le code perl qui montre le côté "bloquant" de la chose, ou bien cette discussion est close.

        et que tu es donc obligé de définir une méthode pour ton callback, alors que le code très court se prêtait particulièrment bien à être défini par une fonction anonyme...

        ??? Je ne définis rien du tout, et j'ai bien associé mon callback à une fonction anonyme. Le code incriminé est

        dialog.rButton.connect('clicked',
        lambda x,d=dialog: setattr(d,'result',2) & d.quit())


        J'ai souligné le lambda, il était peut-être pas assez visible ?

        On pourrait d'ailleurs au passage noter la verbosité de devoir écrire "self."

        Mais oui, bien sûr !
        Écris-moi donc l'équivalent de ce programme en perl, afin qu'on puisse noter la verbosité de devoir écrire $,@,%,->,& à tout bout de champ...
        Sérieusement, s'il te reste plus que ça comme argument...
  • # Re: message d'erreur totalement cryptique

    Posté par  . Évalué à 2.

    Je réponds ici à l'une des critiques de python par gc (voir plus haut), que j'espère toujours convaincre de laisser tomber ses one-liners à 2 balles en perl pour passer à python ;)
    
    
    message d'erreur totalement cryptique
    
    
     Si tu écris cela :
     
    
    class Foo:
        def __init__(self):
            print "Foo"
     
    class Bar(Foo):
        def __init__(self):
            super(Bar, self).__init__()
            print "Bar"
     
    b = Bar()
    
    Tu te prends comme message d'erreur :
     
     File "foo.py", line 7, in __init__
     super(Bar, self).__init__()
     TypeError: super() argument 1 must be type, not classobj
    
    
    
    Read The Fine Manual
    
    
    La syntaxe par défaut pour appeler le constructeur de la classe parente est la suivante:
    
    class Foo:
        def __init__(self):
            print "Foo"
     
    class Bar(Foo):
        def __init__(self):
            Foo.__init__(self)
            print "Bar"
    
    Ce qui invalide ta critique n°2.
    • [^] # Re: message d'erreur totalement cryptique

      Posté par  (site web personnel) . Évalué à 2.

      Sur ce point j'avais "trouvé" la façon d'appeler le constructeur père sur google : je blamerais donc tes amis pythoniens qui ont parlé de cette façon "fausse" (qui fonctionne tout en ne fonctionnant pas, ce qui est amusant) et ceux qui ont mis un pagerank suffisamment haut pour que je tombe dessus et qui donc pensaient que c'était une très bonne façon de faire. Mais l'essentiel est qu'effectivement, cette critique tombe.
  • # Re: interpolations

    Posté par  . Évalué à 2.

    Je réponds ici à la critique n°3 de python par gc, qui s'avère sans fondement.

    Il n'y a pas d'interpolation dans les chaînes de caractères. Quelque chose de si pratique disponible dans tellement de langages de script (même Tcl !) et qui n'existe pas en Python, je trouve ça vraiment très dommage. On pourra me dire que ça rend le programme plus lisible, mais au contraire, devoir passer "de droite à gauche" comme on le fait dans un format en C ne rend que le programme moins lisible. C'est un détail mais c'est prodigieusement agaçant quand on programme avec Python.

    ahem...

    print "Scoop: gc va bientôt laisser tomber %(langageIllisible)s pour %(langageCool)s ..." % {'langageIllisible':'perl', 'langageCool':'python'}

    Scoop: gc va bientôt laisser tomber perl pour python ...
    • [^] # Re: interpolations

      Posté par  (site web personnel) . Évalué à 2.

      Je ne vois pas de différence avec ce que je disais, on se retrouve encore une fois à devoir regarder de gauche à droite à gauche pour savoir qu'est-ce qui renseigne quel élément du format...

      Ç'aurait été tellement plus simple de pouvoir écrire :

      print "Scoop : gc va bientot $foo pour $bar"

      ou :

      print "Scoop : gc va bientot #{foo} pour #{bar}"

      ou encore, si Python avait pu bénéficier de plus de bonnes idées :

      print "Scoop : gc va bientot %(foo) pour %(bar)"

      Mais il semble que ce soit Perl ou Ruby qui trustent les idées qui simplifient la vie des programmeurs...

      Scoop: gc va bientôt laisser tomber perl pour python...

      C'est pas demain la veille :)
      • [^] # Re: interpolations

        Posté par  . Évalué à 2.

        Je ne vois pas de différence avec ce que je disais

        Là je dis chapeau... être d'aussi mauvaise foi avec autant d'assurance, je m'incline, c'est bien toi le meilleur !

        Tu disais donc, et je te laisse la paternité d'une telle affirmation, qu'avec

        "Scoop: gc va bientôt laisser tomber %(langageIllisible)s pour %(langageCool)s ..."

        il n'y a pas d'interpolation, et ça ne se lit pas de gauche à droite...

        Sans commentaire !
  • # Re: objet ? pas objet ?

    Posté par  . Évalué à 2.

    Je réponds ici à la quatrième et dernière critique de python par gc ci-dessus (http://linuxfr.org/comments/444523.html#444523(...))


    On fait la publicité de Python comme langage objet. Mais pourquoi n'ont-ils pas fait les choses jusqu'au bout ?


    Là, c'est quand même le ponpon ! Tu dis préférer perl à python, et dans le même temps tu critiques python en tant que langage objet... chapeau, faut oser !

    Notons que je n'affirme pas que Perl est mieux là-dessus, Perl est une catastrophe pour l'objet

    encore heureux... en python tout est objet ; en perl, objectivement je sais même pas si on peut dire qu'on puisse faire de quelque chose un objet. Sérieusement, la POO en perl est une expérience cauchemardesque que je préfère oublier. C'est dommage, c'est même un vrai gâchis, perl aurait pu être un super langage. Pour reprendre les mots de Larry Wall, perl est ce que la communauté en a voulu...

    C'est objet mais pour avoir la longueur d'une chaîne, là où Ruby a fait le boulot correctement (chaine.length) en Python c'est len(chaine).


    s="En python aussi, gc..."
    print s.__len__()
    22

    Conversion d'une chaine vers un entier ? En Ruby je fais chaine.to_i en Python rebelote je dois faire int(chaine).


    int est une fonction fabrique (design pattern : factory): elle crée un objet de classe int... je vois pas plus objet comme approche.

    Par ailleurs je sais que tu sais (et tu le sais...) qu'il n'y a aucune différence fondamentale entre les deux formes f(a) et a.f(), c'est juste du sucre syntaxique, comme savent si bien dire les perliens.

    Enfin, je me répète, mais le plus important est bien qu'en python, tout soit objet. Si la syntaxe par défaut ne te plaît pas, tu peux toujours ajouter les méthodes de ton choix aux classes natives afin de pouvoir écrire "128".to_i

    Donc tes arguments avancés sur ce point ne tiennent pas.
    J'invalide le point n°4.

    Au passage, je n'imagine pas me passer de surcharge d'opérateurs, simplissime à mettre en oeuvre en python. Comment peux-tu survivre en perl sans ça ?


    Conclusion

    Et voilà ma conclusion sur Python, je disais que l'absence de fonctionnel l'alimenterait ; les points ci-dessus aussi.


    J'ai répondu aux quatre critiques avancées et j'ai montré qu'elles ne tenaient pas.
    Ça n'était donc que ça qui te retenait de passer à python ? Parfait, bienvenue au club !
    • [^] # Re: objet ? pas objet ?

      Posté par  (site web personnel) . Évalué à 2.

      Si on pouvait vraiment parler de sucre syntaxique à propos de int(string), alors string.int() serait possible ce qui n'est pas le cas, donc le sucre n'a pas grand chose à voir ici. int() est un builtin, point, et c'est une décision dont je ne vois aucun intérêt, mais tu vas peut-être pouvoir m'éclairer. Ce qui fait en partie la force d'un langage malgré ce que pensent beaucoup, c'est sa bilbiothèque standard. Python a bien joué, il possède une bibliothèque standard très étendue. Mais il faut aussi qu'elle soit de qualité ; et une des qualités c'est l'orthogonalité, la logique qu'elle montre. L'ensemble des opérations de base sur les chaînes doivent être disponibles en tant que méthode sur les instances de cette classe, ça me paraît peu discutable, quand même ?

      Tout est objet dans Python, mais manque de bol pour une fonction fondamentale comme la longueur d'une chaine il faut écrire 4 underscores pour obtenir la fonction. Pourquoi, une méthode len() c'était trop simple ? Ah non __len__() c'est mieux, ça va amuser les programmeurs de tapper 4 underscores, et ça va faire des programmes très lisibles puisque la qualité de Python c'est de donner des programmes lisibles...

      Quant à la surcharge d'opérateurs, étant donné que Perl se prête moyennement à l'objet... Mais j'ai déjà dit et répété que Perl suxait pour pas mal de points, et pour l'objet c'est clair que c'est une de mes critiques les plus importantes (relis mon dernier post plus haut, je n'ai jamais affirmé que Perl était la panacée pour les gros programmes, surtout ceux qui se prêtent particulièrement à l'objet). Si je veux faire un programme conséquent en objet, qui n'a pas besoin impératif de performance ou de vérifications fortes avant l'exécution, je choisis Ruby, bien sûr.

      J'ai répondu aux quatre critiques avancées et j'ai montré qu'elles ne tenaient pas.
      Ça n'était donc que ça qui te retenait de passer à python ? Parfait, bienvenue au club !


      Euh, bof :) je consens que le problème sur l'appel du constructeur père était du à mon ignorance de Python ; sur les closures tu as au contraire montré toi-même qu'elles n'étaient pas dispo et qu'on obtenait un programme long et verbeux (je peux faire du C pour faire ça merci ;p) ; sur l'interpolation des chaînes tu as au contraire montré toi-même que ce n'était pas possible ; et enfin sur le manque d'"orthodoxie objet" sur la classe string, ta réponse est de t'émerveiller d'une méthode __len__() et d'un builtin int(), en disant que si on n'est pas content on peut écrire ces méthodes soi-même (je me demande l'utilité d'écrire des méthodes dans une bibliothèque de base si c'est pour ensuite proposer aux gens d'en écrire d'autres soi-même).
      • [^] # Re: objet ? pas objet ?

        Posté par  . Évalué à 2.

        Pourquoi, une méthode len() c'était trop simple ? Ah non __len__() c'est mieux, ça va amuser les programmeurs de tapper 4 underscores, et ça va faire des programmes très lisibles puisque la qualité de Python c'est de donner des programmes lisibles...

        C'est clair qu'avec ce genre de coupage de cheveu en quatre on fait vachement avancer le débat. Après cette longue discussion on en revient donc à l'essentiel selon toi, le nombre de caractères à taper ?
        C'est marrant parce si je reprends ton argument ça doit bien te faire chier de taper des trucs comme ça en perl, non (pris au hasard dans /usr/lib/perl5) ?

        $self->{structure} = new Text::BibTeX::Structure ($structure, @options);

        Elle est où, la concision et la lisibilité, là ? Allo ??


        Si je veux faire un programme conséquent en objet, qui n'a pas besoin impératif de performance ou de vérifications fortes avant l'exécution, je choisis Ruby, bien sûr.

        Alors, résumons-nous.
        - tu viens dans le forum python vanter les merveilles de perl, en présentant un exemple de code, et tu mets au défi les lecteurs de ce forum de te proposer
        un équivalent en python
        - je te propose mon implémentation et te demande ce que tu en penses (cela répond-il au besoin ? Que dire de l'approche one-liner vs adoption sytématique de code objet et de documentation, a fortiori au sein d'un projet ? Lisibilité des deux implémentations ?)
        - sans te prononcer sur les questions ci-dessus, ce qui était l'objet supposé, ou à prévoir, de ton troll, tu te lances dans une critique enflammée du seul langage python en avançant des points dont, après leur développement, je laisse aux autres lecteurs le soin de juger de la pertinence ; moi je fatigue un peu.
        - dans un tel contexte, tu inclus à tes critiques l'approche objet de python... mais mise en regard avec celle de perl (si tant est qu'il en existe une), tu retournes ta veste et tu dis que tu choisis Ruby, bien sûr quand le besoin s'en fait sentir... tu serais pas comme Dutronc, à retourner ta veste toujours au bon moment ?

        Du coup, tu veux montrer / prouver / proposer quoi exactement ?
        • [^] # Re: objet ? pas objet ?

          Posté par  (site web personnel) . Évalué à 2.

          - tu viens dans le forum python vanter les merveilles de perl, en présentant un exemple de code, et tu mets au défi les lecteurs de ce forum de te proposer
          un équivalent en python
          - je te propose mon implémentation et te demande ce que tu en penses (cela répond-il au besoin ? Que dire de l'approche one-liner vs adoption sytématique de code objet et de documentation, a fortiori au sein d'un projet ? Lisibilité des deux implémentations ?)
          - sans te prononcer sur les questions ci-dessus, ce qui était l'objet supposé, ou à prévoir, de ton troll, tu te lances dans une critique enflammée du seul langage python en avançant des points dont, après leur développement, je laisse aux autres lecteurs le soin de juger de la pertinence ; moi je fatigue un peu.


          On a deux problèmes.

          Le premier, c'est le fait que lorsqu'on entend si souvent "abandonnez Perl et utilisez Python", c'est dommage et ça saute surtout aux yeux dans la situation des one-liners. Dans l'exemple que je proposais tout en haut, on a un programme Perl qui s'écrit en quelques minutes alors que l'équivalent en Python demande plusieurs fois plus de temps (j'aurais pu m'abstenir du terme "chiant" pour ne pas heurter ta sensibilité, je m'en excuse). Je ne discute plus du problème de pérénité du code, comme je l'ai expliqué déjà plus haut.

          Le deuxième, c'est lorsque tu me demandes "tu n'es pas tenté [par Python] ?" : à ce moment-là, je me fends d'une explication bien plus longue expliquant pourquoi, pour moi, non je ne suis pas tenté par Python - et bien sûr, ça va bien plus loin que l'écriture de one-liner, heureusement. J'écris la collection des "problèmes" que j'ai rencontrés en utilisant Python, et d'ailleurs certains n'en étaient pas, ce que je suis parfaitement disposé à reconnaître. Sur le "débat" très long qui s'en suit, je fatigue autant que toi et je vais donc laisser à l'hypothétique lecteur (faut pas rêver) le soin de se débrouiller avec tout ce qui a déjà été dit sans en rajouter.

          - dans un tel contexte, tu inclus à tes critiques l'approche objet de python... mais mise en regard avec celle de perl (si tant est qu'il en existe une), tu retournes ta veste et tu dis que tu choisis Ruby, bien sûr quand le besoin s'en fait sentir... tu serais pas comme Dutronc, à retourner ta veste toujours au bon moment ?

          Voir ci-dessus. Je n'ai jamais retourné ma veste, relis le contenu. Je n'ai jamais dit que Perl était supérieur à Python en objet, j'ai seulement dit que pour les one-liner Perl était (largement) supérieur à Python, et que pour les "vrais" programmes j'ai rencontrés quelques problèmes avec Python qui ne m'ont pas plu ; mon choix actuel pour les programmes pour lesquels on peut choisir un langage de script se portant sur Perl (car je le connais bien et qu'il est raisonnablement satisfaisant) et/ou Ruby (car il est bien plus élégant que Python à mon humble avis, et semble avoir moins de problèmes que Python).

          Du coup, tu veux montrer / prouver / proposer quoi exactement ?

          Moi aussi je fatigue de répéter toujours la même chose :)
          • [^] # Re: objet ? pas objet ?

            Posté par  . Évalué à 1.

            l'hypothétique lecteur (faut pas rêver)

            Y'en avait encore un ouais. D'ailleurs il est globalement assez d'accord avec toi : perl l'emporte sans pb sur python en matière de oneliners (c'est pas bien dur), et puis pour ce qui est des vrais programmes, bah la question est pas vraiment pertinente puisque c'est de toute façon ruby qui étale tout le monde. Et puis voilà.
  • # Je suis pas sùr mais ça concerne votre truc, non ?

    Posté par  (site web personnel) . Évalué à 2.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.