Journal Indication de type pour Python

Posté par . Licence CC by-sa
Tags :
16
26
déc.
2014

Python est connu pour son typage dynamique et son approche duck typing qui permet décrire des fonctions gérant plusieurs types de données sans même y penser.

>>> fun = lambda a, b, c: (a + b)*c
>>> fun(1, 2, 3)
9
>>> fun('pommes et ', 'oranges ', 3)
'pommes et oranges pommes et oranges pommes et oranges '
>>>

Sauf que le problème est là. Une variable pouvant être de n'importe quel type, il faut aussi s'attendre à recevoir n'importe quel type et dans le cas échéant filtrer sur les types que l'on désir traiter.

>>> def fun2(a):
...     it = []
...     if isinstance(a, str):
...         it.append(a)
...     else:
...         try:
...             it = iter(a)
...         except TypeError:
...             it.append(a)
...     for i in it:
...         print(i)
...
>>> fun2('a')
a
>>> fun2(1)
1
>>> fun2([1, 2, 3])
1
2
3
>>>

C'est tout de suite moins sexy et si l'on reçoit un type que l'on n'attendait pas, cela cause le plus souvent un arrêt du programme lors de l'exécution. Plutôt gênant quand on sait que les langages à typage statique n'ont pas ce problème car toute erreur de type est décelée à la compilation.

Que faire ? Le plus souvent, le paradigme de typage statique ou dynamique fait partie du cœur du langage et rares sont ceux qui permettent de mixer les deux.

Le projet mypy permet déjà d’annoter les éléments du langage afin du vérifier statiquement l'usage des types. Des discussions sont actuellement en cours afin d'intégrer ces mêmes fonctionnalités au cœur du langage.

Il sera ainsi possible d'annoter les fonctions afin de renseigner sur les types acceptés et le type de retour.

def foo(a: str, b: str, c: int):
    pass

La solution actuellement proposée par Guido van Rossum (créateur de Python) pour annoter les variables est d'utiliser les commentaires.

a = 1 # type: int

Une solution moins élégante que celle actuellement proposée par mypy.

Une chose est sûre, l'indication des types restera optionnelle et ne doit aucunement changer le langage. Toute erreur générée par l'analyse statique des types ne serait que purement informelle et ne causerait pas l'arrêt du programme.

  • # Bonne nouvelle

    Posté par . Évalué à 6.

    J'ai écrit une grosse lib en bash pour le boulot et à chaque fonction la moitié du code c'est pour valider les arguments. Typage et nombre aussi car aucun prototypage, même problème en Perl.

    C'est quand-même lourd à gérer, bon l'avantage c'est que tu commente abondamment tes fonctions du coup, mais c'est à 90% du commentaire inutile, car il remplace l'absence l'auto-documentation d'un bon prototype.

    • [^] # Re: Bonne nouvelle

      Posté par . Évalué à 1.

      C'est un des avantages d'Erlang et de son mode de programmation monitor/worker par rapport à Python ou Ruby : si ça plante tu laisse planter, et le moniteur relance le worker.

      • [^] # Re: Bonne nouvelle

        Posté par . Évalué à 4.

        Oui enfin programmer en Erlang comme ça ça demande quand même d'organiser ton programme de façon complètement différente, c'est un autre paradigme de programmation et ça ne se prête pas à n'importe quelle application !

      • [^] # Re: Bonne nouvelle

        Posté par (page perso) . Évalué à 10.

        Je ne vois pas trop ce que ça change : ça relancera peut-être le worker, mais il aura toujours des arguments incorrects donc ça ne fonctionnera pas plus (sauf que tu ne te rendras pas compte qu'il y a un bug, ce n'est pas vraiment sain comme méthode).

        • [^] # Re: Bonne nouvelle

          Posté par . Évalué à 2.

          C'est très sain, ça évite la programmation défensive et ça t'assure que ton service sera toujours dispo. Et tu peux loguer les plantages pour débugger. Et enfin, je suppose que si en amont un process attend une réponse d'un worker qui ne lui arrive pas, il ne va pas passer son temps à renvoyer la même demande (ou alors il faut pendre le développeur qui a pondu ça).

          • [^] # Re: Bonne nouvelle

            Posté par . Évalué à 2.

            Tu peux loger les problèmes et surtout les corriger en hotplug avec Erlang.

          • [^] # Re: Bonne nouvelle

            Posté par . Évalué à 5.

            En fait on parle pas de la même chose je pense : pour moi (et flan apparemment), on parle d'un cas d'utilisation où tu programmes une application où il y a par exemple une méthode/fonction appelée avec des arguments venant d'une source non contrôlée, genre un utilisateur : dans ce genre de cas, le paradigme mis en avant par Erlang ne sert à rien.

            Toi tu parles d'une situation (théorique à mon gout) où comme un acteur envoi à une autre des arguments incorrects, il fait planter l'autre, mais l'autre et relancé et le premier acteur "apprend" (d'une façon ou d'une autre) de son erreur et ne recommence pas, ou on "change" le code de l'acteur qui a planté et ça recommence pas.

            Mais bon, c'est bien joli dans la théorie, mais dans la pratique, toute la difficulté va être cet "apprentissage" dont je parle (et j'inclue là dedans un apprentissage simple tout comme un apprentissage complexe, je ne suis pas en train de parler d'IA hein) ou encore de gérer le fait que ce qui a déjà craché ne peut pas être oublié (parce que si c'est ta déclaration d'impôts qui a crashée, tu vas pas trop aimer que le worker qui devait la prendre en compte ait été redémarré et que, dans le pire des cas elle soit zappée, et dans le meilleur des cas tu doivent la refaire :)
            Alors c'est sûr que la question de l’interprétation des paramètres devient secondaire dans ce cadre, mais c'est une autre discussion et tu peux pas juste dire : easy on va changer le paradigme et faire restarter les méthodes/fonctions qui crashent ou easy on va changer le code qui crash at runtime.

            • [^] # Re: Bonne nouvelle

              Posté par . Évalué à 3.

              Toi tu parles d'une situation (théorique à mon gout) où comme un acteur envoi à une autre des arguments incorrects, il fait planter l'autre, mais l'autre et relancé et le premier acteur "apprend" (d'une façon ou d'une autre) de son erreur et ne recommence pas, ou on "change" le code de l'acteur qui a planté et ça recommence pas.

              J'ai déjà vécu la situation qu'il décrit. Nos workers traitent des messages qui sont en file d'attente. Lorsque le worker ne s'acquite pas du message, il est remis en file d'attente. Au bout d'un certain nombre de fois, le message est mis dans une file a part qui n'est pas traitée et on analyse "à la main" pourquoi ca a planté. Ce n'est pas parfait, mais c'est suffisant pour garantir une bonne dispo des workers et une tracabilité des erreurs.

            • [^] # Re: Bonne nouvelle

              Posté par . Évalué à 3.

              Mais bon, c'est bien joli dans la théorie, mais dans la pratique, toute la difficulté va être cet "apprentissage" dont je parle (et j'inclue là dedans un apprentissage simple tout comme un apprentissage complexe, je ne suis pas en train de parler d'IA hein) ou encore de gérer le fait que ce qui a déjà craché ne peut pas être oublié (parce que si c'est ta déclaration d'impôts qui a crashée, tu vas pas trop aimer que le worker qui devait la prendre en compte ait été redémarré et que, dans le pire des cas elle soit zappée, et dans le meilleur des cas tu doivent la refaire :)

              Bon je peux te parler de ça parce que je sais comment ça marche. Pas pour la déclaration d'impôt mais pour d'autres démarches comme le changement de domicile. C'est asynchrone et ça utilise les même principes mais c'est pas du erlang. C'est des files JMS après un certains nombre d'erreur, le message est redirigé vers un autre worker et quoi qu'il arrive ta démarche n'est pas validée tant que tu n'a pas d'acquittement.

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: Bonne nouvelle

                Posté par . Évalué à 2.

                Oui je sais comment ça marche, je bosse aussi dans ce genre de trucs, mais ça reste quand même un problème ouvert de savoir comment gérer le "rejeu" quand certains des services humains ou informatique échouent à traiter un truc dans un processus plus grand.

                Je dis pas que ce qui existe est nul, mais on doit pouvoir faire mieux certainement…

                Enfin bon, toute la discussion était que c'est un changement de paradigme (qu'on l'appelle SOA, orienté messages, orienté acteur voire orienté agent), et que c'est pas ça qui va venir concurrencer la résolution du problème du typage d'une fonction :)

  • # Dart

    Posté par . Évalué à 3.

    Merci de ton journal !

    Que faire ? Le plus souvent, le paradigme de typage statique ou dynamique fait partie du cœur du langage et rares sont ceux qui permettent de mixer les deux.

    Les optional types de Dart se rapprochent de ça :

    https://www.dartlang.org/articles/optional-types/
    https://www.dartlang.org/articles/why-dart-types/

  • # Pour compléter...

    Posté par (page perso) . Évalué à 6.

    Le typage des arguments des fonctions en Python c'est intégré dans Python 3 depuis 2006 : https://www.python.org/dev/peps/pep-3107/

    PyCharm utilise d'ailleurs ce typage pour proposer de la complétion intelligente : https://www.jetbrains.com/pycharm/webhelp/type-hinting-in-pycharm.html

    • [^] # Re: Pour compléter...

      Posté par (page perso) . Évalué à 6.

      Ça, c'est la théorie.

      En pratique, ça ne marche pas bien.
      J'ai une classe A définie dans un module a.py, et une classe B définie dans b.py.
      Comment puis-je faire pour que A ait une méthode qui prenne un B en argument, et que B ait une méthode qui prenne un A en argument ?
      (vu que ça nécessite que a.py import b.py, et que b.py importe a.py…)

      De plus, c'est mal pratique pour définir des types un peu complexes (une liste avec différents types, un dictionnaire de dictionnaires de AxB, …)

  • # Formulation...

    Posté par (page perso) . Évalué à 3.

    Sauf que le problème est là. Une variable pouvant être de n'importe quel type, il faut aussi s'attendre à recevoir n'importe quel type et dans le cas échéant filtrer sur les types que l'on désir traiter.

    Non, ce n'est pas un problème, c'est une fonctionnalité. Si tu veux faire des fonctions qui gèrent plusieurs types de manière transparente bah c'est cool. Et t'es pas obligé de filtrer, tu peux faire comme avec les langages un peu plus typés : faire plusieurs fonctions pour gérer les différents types.

    Le seul problème c'est l'absence de vérification du typage au moment de la compilation pour attraper au plus tôt des problèmes. Mais si le code n'est pas buggué, ce n'est pas un problème.

    • [^] # Re: Formulation...

      Posté par . Évalué à 2.

      Oui cela implique de bien documenter sa fonction. Une fonction aussi simple que celle-ci peut apporter quelques surprises si on lui donne de mauvais type de paramètres et en même temps créer plusieurs fonctions par type est un peu overkill.

      fun = lambda a, b, c: (a + b)*c
    • [^] # Re: Formulation...

      Posté par . Évalué à 10.

      Mais si le code n'est pas buggué, ce n'est pas un problème.

      "LOL"

      Et pour avoir du code pas bugger il vaut mieux… le vérifier : tu peux la faire à la main, mais c'est quand même plus agréable d'allouer 100% de son cerveau à la logique métier plutôt que sur les problèmes de types ou de syntaxe :)

      • [^] # Re: Formulation...

        Posté par . Évalué à -4.

        c'est quand même plus agréable d'allouer 100% de son cerveau à la logique métier plutôt que sur les problèmes de types ou de syntaxe :)

        Si je codais du c++ je dirais exactement la même chose : / Mais je pesterais aussi contre les pointeurs, les messages d'erreur incompréhensible ect ect En fait cela me traumatiserait tellement que je crois que j'essaierais d'oublier cette expérience

        heureusement pour moi javascript est là : D AAAahh on respire : )

      • [^] # Re: Formulation...

        Posté par (page perso) . Évalué à 6.

        Mais pour vérifier, au lieu de te reposer sur la compilation, tu te reposes sur les tests unitaires. Ma dernière phrase était une boutade de toute façon, pas la peine de chercher trop loin :D.

        Ce qu'il faut retenir de mon message c'est que le problème n'est pas, comme le laisse supposer le journal, le fait qu'on puisse coder une fonction sans se limiter au niveau des types. Le problème c'est qu'à cause de ça, on ne peut pas attraper rapidement des erreurs triviales. Et je crois qu'on est d'accord.

    • [^] # Re: Formulation...

      Posté par . Évalué à -6.

      Non, ce n'est pas un problème, c'est une fonctionnalité. Si tu veux faire des fonctions qui gèrent plusieurs types de manière transparente bah c'est cool.

      LOL ? (oui tu racontes de la merde si tu te pose la question)

      • [^] # Re: Formulation...

        Posté par (page perso) . Évalué à 5.

        Ce qui me gène c'est cette phrase :

        Sauf que le problème est là. Une variable pouvant être de n'importe quel type, il faut aussi s'attendre à recevoir n'importe quel type et dans le cas échéant filtrer sur les types que l'on désir traiter.

        Non on est pas forcément obligé de filtrer, c'est moche. Soit on est en capacité de gérer la chose par polymorphisme et dans ce cas on ne filtre pas. Soit on fait des fonctions différentes. Comme en Java quoi.

        Bref, rajouter de l'annotation de type dans Python c'est très bien cela permettra de catcher certains problèmes en amont des tests. Ce qui me gène c'est qu'on dit que le problème c'est de devoir gérer tous les cas possibles dans la fonction. Non c'est pas vrai, si on n'appelle pas la bonne fonction ça marchera pas c'est tout. Et le problème est simplement qu'en Python ça va casser à l'exécution et pas à la compilation.

        • [^] # Re: Formulation...

          Posté par . Évalué à 2.

          .Non on est pas forcément obligé de filtrer, c'est moche. Soit on est en capacité de gérer la chose par polymorphisme

          Comment tu fait du polymorphisme sans filtrer dans un langage non type?
          Juste parce qu'un objet a une methode "foo" ne veut pas dire que foo a le contrat que tu veux. Genre, add ou delete, c'est le genre de nom assez commun qui peut avoir un sens tres different selon le contexte.

          Linuxfr, le portail francais du logiciel libre et du neo nazisme.

          • [^] # Re: Formulation...

            Posté par (page perso) . Évalué à 6.

            Il parle de l'obligation de filtrer pour distinguer les cas licites des cas invalides, pas pour différencier les différents cas valides (enfin, c'est ce que j'ai compris). L'idée est que si le cas est invalide, bah tant pis, le développeur n'avait qu'à mieux lire la doc :)

            • [^] # Re: Formulation...

              Posté par (page perso) . Évalué à 4.

              Je ne suis pas sûr d'avoir compris le commentaire de Groumly, j'espère que je ne réponds pas à côté. En tout cas je suis d'accord avec flan sur la conclusion.

              @Groumly : lorsque tu fais du polymorphisme en Java, tu ne testes pas les types, cela va appeler la bonne méthode. Et bien en Python c'est la même chose en encore plus flexible. Mais ce qui te gêne c'est donc si on balance un objet qui a le malheur d'avoir une méthode avec le bon nom. Et bien c'est incorrect et ça produira une mauvaise sortie. On peut regretter de ne pas voir le problème dès la compilation d'où le besoin d'annoter les types. Sinon on le verra au niveau des tests unitaires s'ils sont bien faits.

              Donc vraiment, c'est très rare d'avoir besoin de filtrer et surtout ce n'est pas une obligation. Ce n'est pas parce que je fais une fonction foo que je suis obligé de la rendre générique pour tous les types imaginables.

              Si en Java tu as le choix entre deux méthodes A et B avec la même signature alors tu peux remplacer l'appel à la méthode A par la méthode B sans problème de compilation. Ça ne fera certainement pas ce que tu voulais à l'exécution…

              Bon et bien là c'est pareil (en pire car le type n'est pas précisé donc plus simple de se planter) : si tu n'appelles pas la bonne fonction, on ne peut plus rien pour toi.

          • [^] # Re: Formulation...

            Posté par . Évalué à 6.

            D'une part le python c'est typé d'autre part le fait de filtrer sur le type c'est complètement à l'envers de l'objectif de python. En principe tu écris ta méthode qui fait un traitement sur un objet. Tout objet qui répond correctement aux traitement que tu lui fait subir doit passer (même si c'est une instance d'object à qui j'ai dynamiquement ajouté toutes les méthodes que je veux). C'est à l’appelant de s'assurer que les paramètres sont corrects pas à celui qui appel. C'est la base du duck typing et utiliser python autrement c'est faire du gros hacking bien sale avec un langage inadapté. C'est que tu ne veux pas de duck typing ou de typage dynamique, parce que tu refais ce qu'un compilateur fait mieux que toi, c'est à dire s'assurer que les paramètres sont corrects.

            Tu peux le voir différemment un utilisateur de tes méthodes peut très bien construire un objet qui colle très bien à ta méthode et qui corresponsond usage et que tu n'aurais pas imaginé.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Formulation...

              Posté par . Évalué à 3.

              C'est une vision claire de pour quoi est fait python, mais alors moi ce qui m'échappe, c'est comment tu gères justement cette difficulté de fournir l'objet bien "formaté" à la méthode ?
              Parce que pour des petits trucs c'est simple, mais dès qu'il y a un peu de complexité, tu te retrouves à ne plus savoir ce que doit fournir ton objet comme méthode, voire à devoir faire le même travail de filtrage (métier) de ta méthode pour savoir quelle méthodes doit avoir ton objet…

              Ou alors après l'idée c'est "juste" de se reposer sur les tests pour faire cette détection ? Ce que je peux comprendre !

              À noter aussi que des langages comme Scala ont du duck typing, appelé structural typing, où tu dis qu'une méthode ne peut prendre en paramètre qu'un objet qui a telle ou telle méthode ou particularité dans sa structure.
              Évidemment ça a ses limites puisque tu ne peux pas "ajouter" des méthodes à l'objet en cours de vie, donc c'est l'utilisation du structural typing est un peu différent de l'utilisation du duck typing de Python (donc je raconte juste ça pour ajouter de l'information, pas pour infirmer ou réfuter quoi que ce soit).

              • [^] # Re: Formulation...

                Posté par . Évalué à 5.

                Ou alors après l'idée c'est "juste" de se reposer sur les tests pour faire cette détection ? Ce que je peux comprendre !

                C'est ça normalement et le langage te fourni ce qu'il faut pour documenter tes méthodes et faire des tests.

                À noter aussi que des langages comme Scala ont du duck typing, appelé structural typing, où tu dis qu'une méthode ne peut prendre en paramètre qu'un objet qui a telle ou telle méthode ou particularité dans sa structure.

                C'est en fait très différent. L'inférence de type est une analyse statique qui fait que tu as une étape dans la compilation qui te fourni la garanti que le typage de ton programme est valide. Imaginons une méthode qui contient le code :

                if (arg.valide()) {
                    arg.foo();
                } else {
                    arg.bar();
                }
                

                En scala tu ne pourra pas produire de binaire si l'objet arg que tu passe en paramètre n'a pas la méthode valide(), foo() et bar() en python tu as tout à fait le droit de passer un objet qui n'a que les méthodes valide() et foo() (il faut juste que tu sois sûr que valide() renvoi toujours vrai. C'est ce qui fait qu'en python il est impossible d'avoir de vérification statique.

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # PyCharm + commentaires

    Posté par (page perso) . Évalué à 6.

    Personnellement, j'utilise PyCharm, qui tente de faire de l'inférence de type, et je l'aide en rajoutant les commentaires comme conseillés par Guido.
    J'essaie de typer toutes mes variables et fonctions, ça me permet d'avoir d'avoir des alertes dans le code à peu près comme en Java ou dans n'importe quel autre langage typé statiquement. Je utilise le moins possible le typage dynamique.

    • [^] # Re: PyCharm + commentaires

      Posté par . Évalué à 6.

      Et vraie question pas pour troller : ça apporte quoi d'utiliser python dans ce cadre ?

      • [^] # Re: PyCharm + commentaires

        Posté par (page perso) . Évalué à 5.

        Python n'a pas que le typage dynamique comme avantage ;-) (syntaxe concise, écosystème très vaste, …) Accessoirement, j'utilise toujours le typage dynamique, mais uniquement quand j'en ai réellement besoin.
        Par expérience, je gagne du temps en codant quelques lignes de plus grâce à l'aide supplémentaire offerte par les informations de type.

  • # méthode

    Posté par (page perso) . Évalué à 9.

    isinstance n'est pas la fonction qui devrait être utilisée pour ce genre de chose. Un problème immédiat est l'inhéritance

    La méthode élégante et sûre et de tester l'existence de l'attribut/la méthode dont on a besoin avec la fonction hasattr .

    En testant uniquement l'existence des méthodes que l'on utilise on autorise tous les types dérivées ainsi que les types crées de toutes pièces par l'utilisateur mais équivalent du point de vue de la fonction.

    • [^] # Re: méthode

      Posté par . Évalué à 3.

      Vraiment ? Et quand tu as des objets de type fondamentalement différent, avec un nom de méthode identique et potentiellement des arguments totalement différents, ca donne quoi ?

      • [^] # Re: méthode

        Posté par (page perso) . Évalué à 5.

        Et bien ça va planter ou ne pas faire ce que tu voudrais… C'est pourquoi tu dois avoir un nom de fonction et/ou un commentaire qui te dit clairement avec quoi tu peux l'utiliser. C'est pas fait pour éviter les bugs, c'est fait pour permettre plus de flexibilité si nécessaire (genre tout le code est similaire et tu n'as qu'un truc qui dépend de si l'objet a telle ou telle méthode).

        • [^] # Re: méthode

          Posté par . Évalué à 3.

          Ouais enfin en disant ça, tu ré-inventes ce à quoi servent les types… sauf qu'au lieu de reposer sur une gestion de la cohérence de ton modèle qui se passe dans ta tête, les types ont toute une théorie mathématique qui permet de faciliter beaucoup de choses.

          Je dis pas que python ou le typage dynamique c'est mal, mais juste que si tu te retrouves à réinventer le concept de types alors il y a un peut-être un problème dans ton choix de langage :)

          • [^] # Re: méthode

            Posté par (page perso) . Évalué à 4.

            Après, le système de types a deux intérêts (à mes yeux) : le premier est pour le programmeur vu que ça l'aide à ne pas faire d'erreurs, le second est pour le compilateur qui permet de faire un code bien plus optimisé.
            Le second n'est pas trop utile pour Python même si c'est toujours bon à prendre : la vitesse n'est de toute façon pas le premier critère de choix quand on fait du Python.
            Le premier peut être mis en place différemment, et peut n'être utilisé que dans l'IDE. Quand je code en Java/Scala ou en Python, la vérification des types se fait de toute façon avant la compilation (et je m'en moque un peu qu'ils soient écrits dans des commentaires ou dans le code). Par contre, ça impose d'utiliser un IDE correct (et tout le monde n'aime pas ça).

            • [^] # Re: méthode

              Posté par . Évalué à 2.

              Pour le compilateur on comprend tous comment ça peut aider, pour le programmeur je ne vois pas trop.
              Dans l'exemple donné foo(a: str, b: str, c: int), ok a et b ne peuvent être que des str, mais on peut toujours les inverser sans que le compilateur ou l'IDE nous aide.
              Bref, ça veut remplacer la doc mais ça ne la remplace que médiocrement car c'est une syntaxe fait pour un compilateur, pas pour un humain.

              • [^] # Re: méthode

                Posté par . Évalué à 4.

                Bon pour des string je sais pas, mais généralement, ce genre de fonction/méthode sont des utilitaires qui sont éprouvés par le temps, documentés et dont on a l'habitude.
                En revanche pour du code plus métier qui touche à, par exemple, des fonctionnalités originales, c'est souvent des types un peu plus complexe qui sont passé en paramètre, ou simplement des modèles simples mais représentés par des types différents.
                Le type représente la sémantique des paramètres, et le compilateur vérifie cette sémantique !
                D'où l’intérêt du typage :)

                • [^] # Re: méthode

                  Posté par . Évalué à 2.

                  Dans le cas de types plus complexes, le premier test va lever l'erreur encore plus vite.

                  Le seul cas où j'avoue que ça manque c'est lors d'une refactorisation importante d'un code qui n'est pas couvert par assez de tests unitaires.

                  • [^] # Re: méthode

                    Posté par (page perso) . Évalué à 4.

                    Pourquoi attendre un test alors qu'on peut avoir l'information dès qu'on écrit la ligne de code ?
                    Personnellement, ajouter l'info de type m'a permis plus d'une fois de corriger un bug avant même de faire le moindre test.
                    Ça ne remplace pas la doc, les deux informations sont complémentaires (d'autant que la documentation fait apparaître le type, et permet d'avoir un lien vers le type attendu).

                    • [^] # Re: méthode

                      Posté par . Évalué à 3.

                      Pourquoi attendre un test ? Parce que dans tous les cas il faudra bien attendre un test, le contrôle du type étant loin d'être suffisant.
                      Comme tu dis ça ne remplace ni la doc ni les tests donc ça fait doublon, triplon même.
                      Et surtout ça ne permet pas de bénéficier des avantages du typage dynamique. Je pense que l'incompréhension vient du fait de ne pas tirer parti du typage dynamique et d'y voir qu'une sorte de manque à rattraper, comme on le voit dans les exemples donnés.
                      Si je prend print par exemple, je n'ai pas à lui donner un string en paramètre et il n'a pas non plus à tester quel type est le paramètre, il appelle simplement __str__() de l'objet passé. Au pire, comme l'indique manawy, on utilise beaucoup plus hasattr que isinstance.

                • [^] # Re: méthode

                  Posté par (page perso) . Évalué à 3.

                  Le type représente la sémantique des paramètres, et le compilateur vérifie cette sémantique !

                  c'est vrai pour du c, pas pour du python
                  En python la convention c'est le duck-typing, ou la sémantique c'est les attributs/méthodes offerts par la classe au moment de son utilisation.
                  Python est dynamique, le nombre de méthodes/d'attributs peut changer au cours de l'utilisation d'un objet (volontairement, ou non). C'est pourquoi si on veut vraiment être sur que tout se passe bien, il faut tester l'existence de la méthode/l'attribut au moment de son utilisation.

                  • [^] # Re: méthode

                    Posté par . Évalué à 3.

                    Nan mais bien sûr, je parle justement de langages typés à la compilation !

          • [^] # Re: méthode

            Posté par (page perso) . Évalué à 4.

            Je dis pas que python ou le typage dynamique c'est mal, mais juste que si tu te retrouves à réinventer le concept de types alors il y a un peut-être un problème dans ton choix de langage :)

            L'intérêt du Python ne réside pas uniquement dans son typage dynamique, et on peut aimer faire du Python sans adhérer complètement au langage. La plupart du temps je pourrais préciser un type pour mes variables sauf quand une fonction utilise une méthode en particulier (c'est un peu plus flexible qu'une interface ou que de l'héritage multiple pour moi, donc c'est compliqué d'y affecter un type).

            En tout cas, si le papa de Python travaille sur le type hinting pour Python, je ne pense pas qu'il s'agisse véritablement d'un problème de choix du langage.

      • [^] # Re: méthode

        Posté par (page perso) . Évalué à 8.

        c'est ça la définition du duck-typing, c'est tès flexible, mais aussi très dangereux, et c'est à l'utilisateur de vérifier que ces types dérivées, ou crées de toutes pièces sont conformes aux attentes.

        si ça ne te plait pas (et moi-même je vois quelques raisons pour quoi) et bien python n'est pas fait pour toi. Et ce type de librairie ne fera que rajouter des problèmes.

        Je parle d'expérience, le duck-typing il faut l'embrasser complétement ou pas du tout. Sinon ça donne du code forcément bancal et vraiment dangereux (parce que plus personne ne sait son rôle et ce qu'il doit vérifier)

        Et les problèmes sont mitigées par le fait qu'aucune erreur n'est silencieuse en Python et une erreur 'AttributeError' sera levé et remonté si l'attribut n'existe pas mais est quand même appelé, cela laisse une chance à l'utilisateur de traiter correctement le problème et/ou de corriger son code.

      • [^] # Re: méthode

        Posté par . Évalué à 4.

        C'est la responsabilité de l'utilisateur de vérifier qu'il ne fait pas de la merde. Et comme c'est toi je te rappel gentiment que c'est pas un typage statique qui va empêcher l'utilisateur de mettre n'importe quoi en paramètre de fonction :) .

        Ceci dit, bien que je comprends l'intérêt du typage dynamique, je préfère de loin un typage statique.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: méthode

          Posté par (page perso) . Évalué à 4.

          J'ai tendance à être comme toi, mais parfois faire du typage dynamique (voire générer des classes à la volée) est parfois bien pratique. Ça permet (parfois) de beaucoup simplifier le code. Personnellement (mais je ne prétends pas que ça soit LA méthode à suivre), j'essaie de faire comme si Python avait un typage statique fort (90% du temps, à la louche), mais je n'hésite pas à faire du duck-typing et autres joyeusetés quand ça aide vraiment à avoir un code plus concis et plus lisible.

Suivre le flux des commentaires

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