Le python semble à la mode ces jours-ci, et ce n'est pas indigne - car c'est vraiment un langage avec lequel on aime presque se voir attribuer un nouveau problème à résoudre. Mais, comme un homme sage a dit une fois (l'appeler un homme sage uniquement parce que je n'ai aucune idée de qui l'a réellement dit; je ne sais pas s'il était si sage à tous), pour vraiment connaître une langue, on connaît non seulement sa syntaxe, sa conception, etc., ses avantages mais aussi ses inconvénients. Aucune langue n'est parfaite, certaines sont juste meilleures que d'autres.
Alors, quels seraient selon vous, les inconvénients objectifs de Python.
Remarque: je ne demande pas de comparaison de langage ici (c'est-à-dire que C # est meilleur que Python parce que ... yadda yadda yadda) - plus d'un objectif (à un certain niveau) opinion sur les fonctionnalités du langage qui sont mal conçues, si, quelles sont peut-être certaines qui vous manquent, etc. Si vous devez utiliser une autre langue à titre de comparaison, mais uniquement pour illustrer un point qui serait difficile à développer autrement (par exemple pour facilité de compréhension)
J'utilise Python assez régulièrement, et dans l'ensemble je considère que c'est un très bon langage. Néanmoins, aucun langage n'est parfait. Voici les inconvénients par ordre d'importance pour moi personnellement:
C'est lent. Je veux dire vraiment, vraiment lent. Souvent, cela n'a pas d'importance, mais cela signifie certainement que vous aurez besoin d'une autre langue pour ces bits critiques pour les performances.
Les fonctions imbriquées sont en quelque sorte nul, vous ne pouvez pas modifier les variables dans la portée externe. Edit: J'utilise toujours Python 2 en raison du support de la bibliothèque, et cette faille de conception m'énerve énormément, mais apparemment, il est corrigé dans Python 3 en raison de l'instruction nonlocal . Je ne peux pas attendre que les bibliothèques que j'utilise soient portées afin que cette faille puisse être envoyée à la cendre tas d'histoire pour de bon.
Il manque quelques fonctionnalités qui peuvent être utiles à la bibliothèque/au code générique et à mon humble avis, la simplicité est poussée à des extrêmes malsains. Les plus importants auxquels je peux penser sont les types de valeurs définis par l'utilisateur (je suppose qu'ils peuvent être créés avec la magie des métaclasses, mais je n'ai jamais essayé) et le paramètre de fonction ref.
C'est loin du métal. Besoin d'écrire des primitives de threading ou du code noyau ou quelque chose? Bonne chance.
Bien que cela ne me dérange pas le manque de capacité à attraper les erreurs sémantiques à l'avance comme compromis pour le dynamisme qui Python offre, je souhaite qu'il y ait un moyen d'attraper les erreurs syntaxiques et les choses idiotes comme la faute de frappe des noms de variables sans avoir à exécuter le code.
La documentation n'est pas aussi bonne que des langages comme PHP et Java qui ont de solides soutiens d'entreprise).
Je déteste que Python ne peut pas faire la distinction entre la déclaration et l'utilisation d'une variable. Vous n'avez pas besoin de saisie statique pour que cela se produise. Ce serait bien d'avoir une façon de dire "ceci est une variable que je déclare délibérément, et je l'intention d'introduire un nouveau nom, ce n'est pas une faute de frappe ".
De plus, j'utilise généralement des variables Python dans un style à écriture unique, c'est-à-dire que je traite les variables comme immuables et ne les modifie pas après leur première affectation. Grâce à des fonctionnalités telles que la compréhension de liste , c'est en fait incroyablement facile et rend le flux de code plus facile à suivre.
Cependant, je ne peux pas documenter ce fait. Rien dans Python m'empêche d'écraser ou de réutiliser des variables.
En résumé, j'aimerais avoir deux mots clés dans la langue: var
et let
. Si j'écris dans une variable non déclarée par l'un ou l'autre, Python devrait générer une erreur. De plus, let
déclare les variables en lecture seule, tandis que var
les variables sont "normales".
Considérez cet exemple:
x = 42 # Error: Variable `x` undeclared
var x = 1 # OK: Declares `x` and assigns a value.
x = 42 # OK: `x` is declared and mutable.
var x = 2 # Error: Redeclaration of existing variable `x`
let y # Error: Declaration of read-only variable `y` without value
let y = 5 # OK: Declares `y` as read-only and assigns a value.
y = 23 # Error: Variable `y` is read-only
Notez que les types sont toujours implicites (mais les variables let
sont à toutes fins utiles statiquement typées car elles ne peuvent pas être rétablies à une nouvelle valeur, tandis que les variables var
peuvent toujours être typées dynamiquement).
Enfin, tous les arguments de méthode doivent automatiquement être let
, c'est-à-dire qu'ils doivent être en lecture seule. Il n'y a en général aucune bonne raison de modifier un paramètre, à l'exception de l'idiome suivant:
def foo(bar = None):
if bar == None: bar = [1, 2, 3]
Cela pourrait être remplacé par un idiome légèrement différent:
def foo(bar = None):
let mybar = bar or [1, 2, 3]
Ma principale plainte concerne le threading, qui n'est pas aussi performant dans de nombreuses circonstances (par rapport à Java, C et autres) en raison du verrouillage de l'interpréteur global (voir "Inside the Python GIL") (Lien PDF) parler)
Cependant, il existe une interface multiprocessus qui est très facile à utiliser, mais elle sera plus lourde sur l'utilisation de la mémoire pour le même nombre de processus par rapport aux threads, ou difficile si vous avez beaucoup de données partagées . L'avantage, cependant, est qu'une fois qu'un programme fonctionne avec plusieurs processus, il peut évoluer sur plusieurs machines, ce qu'un programme fileté ne peut pas faire.
Je suis vraiment en désaccord sur la critique de la documentation, je pense qu'elle est excellente et meilleure que la plupart sinon toutes les langues principales.
Vous pouvez également attraper de nombreux bogues d'exécution en cours d'exécution pylint .
Discutablement, le manque de typage statique, qui peut introduire certaines classes d'erreurs runtime, ne vaut pas la flexibilité supplémentaire que le typage de canard offre.
Je pense que les parties orientées objet de Python se sentent "boulonnées". Le besoin de passer explicitement "self" à chaque méthode est un symptôme que son composant OOP n'était pas expressément prévu , pourrait-on dire; il montre également les règles de portée parfois verruqueuses de Python qui ont été critiquées dans une autre réponse.
Modifier:
Quand je dis que les parties orientées objet de Python semblent "boulonnées", je veux dire que parfois, le côté OOP semble plutôt incohérent. Prenez Ruby, par exemple: dans Ruby, tout est un objet, et vous appelez une méthode en utilisant la syntaxe obj.method
Familière (à l'exception des opérateurs surchargés, de cours); en Python, tout est aussi un objet, mais certaines méthodes que vous appelez en tant que fonction; c'est-à-dire que vous surchargez __len__
pour renvoyer une longueur, mais appelez-la en utilisant len(obj)
au lieu du plus familier (et cohérent) obj.length
commun dans d'autres langues. Je sais qu'il y a des raisons derrière cette décision de conception, mais je ne les aime pas.
De plus, le modèle OOP de Python ne dispose d'aucune sorte de protection des données, c'est-à-dire qu'il n'y a pas de membres privés, protégés et publics; vous pouvez les imiter en utilisant _
et __
devant les méthodes, mais c'est un peu moche. De même, Python ne donne pas tout à fait l'aspect correct de OOP correct.
Choses que je n'aime pas à propos de Python:
lambda
ne peut contenir qu'une seule expression).cin
ou scanf
en C++ et C ou Scanner
en Java).Arguments par défaut avec des types de données mutables.
def foo(a, L = []):
L.append(a)
print L
>>> foo(1)
[1]
>>> foo(2)
[1, 2]
C'est généralement le résultat de quelques bugs subtils. Je pense qu'il serait préférable de créer un nouvel objet liste chaque fois qu'un argument par défaut était requis (plutôt que de créer un seul objet à utiliser pour chaque appel de fonction).
Edit: Ce n'est pas un gros problème, mais quand quelque chose doit être référencé dans les documents, cela signifie généralement que c'est un problème. Cela ne devrait pas être obligatoire.
def foo(a, L = None):
if L is None:
L = []
...
Surtout quand cela aurait dû être la valeur par défaut. C'est juste un comportement étrange qui ne correspond pas à ce que vous attendez et n'est pas utile dans un grand nombre de circonstances.
Certaines des fonctionnalités de Python qui le rendent si flexible en tant que langage de développement sont également considérées comme des inconvénients majeurs par celles utilisées pour l'analyse statique du "programme entier" menée par le processus de compilation et de liaison dans des langages tels que C++ et Java.
Les variables locales sont déclarées à l'aide de l'instruction d'affectation ordinaire. Cela signifie que les liaisons de variables dans toute autre étendue nécessitent que le compilateur récupère des annotations explicites (déclarations globales et non locales pour les étendues externes, notation d'accès aux attributs pour les étendues d'instance). Cela réduit considérablement la quantité de passe-partout nécessaire lors de la programmation, mais signifie que des outils d'analyse statique tiers (tels que des pyflakes) sont nécessaires pour effectuer des vérifications gérées par le compilateur dans des langages qui nécessitent des déclarations de variables explicites.
Le contenu des modules, des objets de classe et même de l'espace de noms intégré peut être modifié lors de l'exécution. C'est extrêmement puissant, permettant de nombreuses techniques extrêmement utiles. Cependant, cette flexibilité signifie que Python n'offre pas certaines fonctionnalités communes aux langages de type statique OO. Plus particulièrement, le paramètre "self" pour les méthodes d'instance est explicite) plutôt qu'implicite (puisque les "méthodes" ne doivent pas être définies à l'intérieur d'une classe, elles peuvent être ajoutées ultérieurement en modifiant la classe, ce qui signifie qu'il n'est pas particulièrement pratique de passer implicitement la référence d'instance) et les contrôles d'accès aux attributs peuvent ' t être facilement appliqué selon que le code est ou non "à l'intérieur" ou "à l'extérieur" de la classe (car cette distinction n'existe que pendant l'exécution de la définition de classe).
Cela est également vrai pour de nombreux autres langages de haut niveau, mais Python a tendance à faire abstraction de la plupart des détails matériels. Les langages de programmation de systèmes comme C et C++ sont encore bien mieux adaptés pour gérer l'accès matériel direct (cependant, Python s'adressera très volontiers à ceux-ci via des modules d'extension CPython ou, plus facilement, via la bibliothèque ctypes
).
Exemple de cadrage cassé; transcription de la session de l'interprète:
>>> x=0
>>> def f():
... x+=3
... print x
...
>>> f()
Traceback (most recent call last):
File "", line 1, in ?
File "", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
global
et nonlocal
des mots clés ont été introduits pour corriger cette stupidité de conception.
Je trouve la combinaison de python de la syntaxe this.method()
orientée objet et procédurale/fonctionnelle method(this)
très troublante:
x = [0, 1, 2, 3, 4]
x.count(1)
len(x)
any(x)
x.reverse()
reversed(x)
x.sort()
sorted(x)
Ceci est particulièrement mauvais car un grand nombre de fonctions (plutôt que des méthodes) sont simplement déversées dans le espace de noms global : méthodes relatives aux listes, chaînes, nombres, constructeurs, métaprogrammation, le tout mélangé dans un seul grand liste triée par ordre alphabétique.
À tout le moins, les langages fonctionnels comme F # ont toutes les fonctions correctement espacées dans les modules:
List.map(x)
List.reversed(x)
List.any(x)
Ils ne sont donc pas tous ensemble. De plus, c'est une norme suivie dans toute la bibliothèque, donc au moins c'est cohérent.
Je comprends les raisons pour lesquelles la fonction vs méthode , mais je pense toujours que c'est une mauvaise idée de les mélanger comme ça. Je serais beaucoup plus heureux si la syntaxe de la méthode était suivie, au moins pour les opérations courantes:
x.count(1)
x.len()
x.any()
x.reverse()
x.reversed()
x.sort()
x.sorted()
Que les méthodes soient en mutation ou non, les avoir comme méthodes sur l'objet présente plusieurs avantages:
Module
lors de l'appel de Module.method(x)
. En prenant l'exemple de la liste fonctionnelle ci-dessus, pourquoi dois-je répéter sans cesse List
? Il faut savoir que c'est un List
et je ne veux pas appeler la fonction Navigation.map()
dessus! L'utilisation de la syntaxe x.map()
la maintient DRY et toujours sans ambiguïté.Et bien sûr, cela présente des avantages par rapport à la manière de le faire put-everything-in-global-namespace . Ce n'est pas que la méthode actuelle est incapable de faire avancer les choses. C'est même assez concis (len(lst)
), car rien n'est nommé! Je comprends les avantages de l'utilisation des fonctions (comportement par défaut, etc.) par rapport aux méthodes, mais je ne l'aime toujours pas.
C'est juste désordonné. Et dans les grands projets, le désordre est votre pire ennemi.
Manque de homoiconicité .
Python a dû attendre 3.x pour ajouter un mot clé "with". Dans n'importe quelle langue homoiconique, il aurait pu être insignifiamment ajouté dans une bibliothèque.
La plupart des autres problèmes que j'ai vus dans les réponses sont de l'un des 3 types:
1) Choses qui peuvent être corrigées avec des outils (par exemple, pyflakes) 2) Détails de mise en œuvre (GIL, performances) 3) Choses qui peuvent être corrigées avec des normes de codage (c'est-à-dire des fonctionnalités que les gens souhaitent ne pas y avoir)
# 2 n'est pas un problème avec la langue, IMO # 1 et # 3 ne sont pas des problèmes sérieux.
Python est mon langage préféré car il est très expressif, mais vous empêche toujours de faire trop d'erreurs. J'ai encore quelques petites choses qui m'agacent:
Pas de vraies fonctions anonymes. Lambda peut être utilisé pour les fonctions à instruction unique, et l'instruction with
peut être utilisée pour de nombreuses choses où vous utiliseriez un bloc de code dans Ruby. Mais dans certaines situations, cela rend les choses un peu plus maladroites qu'elles ne devraient l'être. (Loin d'être aussi maladroit qu'il le serait en Java, mais quand même ...)
Une certaine confusion dans la relation entre les modules et les fichiers. L'exécution de "python foo.py" à partir de la ligne de commande est différente de "import foo". Les importations relatives dans Python 2.x peuvent également causer des problèmes. Pourtant, les modules de Python sont bien meilleurs que les fonctionnalités correspondantes de C, C++ et Ruby.
self
explicite. Même si je comprends certaines des raisons de cela, et même si j'utilise Python quotidiennement, j'ai tendance à faire l'erreur de l'oublier. Un autre problème avec cela est qu'il devient un peu fastidieux de créer une classe à partir d'un module. L'auto explicite est lié à la portée limitée dont d'autres se sont plaints. La plus petite étendue de Python est l'étendue de la fonction. Si vous gardez vos fonctions petites, comme vous devrait, ce n'est pas un problème en soi et l'OMI donne souvent un code plus propre.
Certaines fonctions globales, telles que len
, que vous attendez d'être une méthode (ce qu'elle est en réalité dans les coulisses).
Indentation importante. Pas l'idée elle-même, ce qui, je pense, est génial, mais comme c'est la seule chose qui empêche tant de gens d'essayer Python, peut-être Python serait mieux avec un début/fin (facultatif)) En ignorant ces gens, je pourrais aussi vivre avec une taille forcée pour l'indentation.
Qu'il ne s'agit pas du langage intégré des navigateurs Web, au lieu de JavaScript.
De ces plaintes, ce n'est que la toute première qui me tient suffisamment à cœur et qui, à mon avis, devrait être ajoutée à la langue. Les autres sont plutôt mineurs, sauf le dernier, ce qui serait génial si ça arrivait!
Python n'est pas complètement mature: le langage python 3.2 à ce moment a des problèmes de compatibilité avec la plupart des packages actuellement distribués (généralement ils sont compatibles avec python 2.5). C'est un gros inconvénient qui nécessite actuellement plus d'efforts de développement (trouver le package requis; vérifier la compatibilité; peser en choisissant un package pas aussi bon qui peut être plus compatible; prendre la meilleure version, la mettre à jour à 3.2 qui pourrait prendre des jours; puis commencer à faire quelque chose d'utile).
Il est probable qu'à la mi-2012, ce sera moins un inconvénient.
Notez que je suppose que j'ai été rejeté par un fan-boy. Lors d'une discussion avec les développeurs, notre équipe de développeurs de haut niveau est parvenue à la même conclusion.
La maturité dans un sens principal signifie qu'une équipe peut utiliser la technologie et être très rapidement opérationnelle sans risques cachés (y compris les problèmes de compatibilité). Les packages tiers [python et de nombreuses applications ne fonctionnent pas sous 3.2 pour la majorité des packages aujourd'hui. Cela crée plus de travail d'intégration, de test, de réimplémentation de la technologie elle-même au lieu de résoudre le problème en cours == technologie moins mature.
Mise à jour pour juin 2013: Python 3 a toujours des problèmes de maturité. De temps en temps, un membre de l'équipe mentionne un package nécessaire, puis dit "sauf qu'il ne s'agit que de 2.6" (dans certains de ces cas, j'ai implémenté une solution de contournement via le socket localhost pour utiliser le package 2.6 uniquement avec 2.6, et le reste de nos outils restent avec 3.2). Même MoinMoin, le wiki pur-python, n'est pas écrit en Python 3.
La portée de Python est gravement cassée, ce qui rend la programmation orientée objet dans Python très maladroite.
Les modificateurs d'accès en Python ne sont pas applicables - il est difficile d'écrire du code bien structuré et modularisé.
Je suppose que cela fait partie de la portée cassée de @ Mason - un gros problème en général avec cette langue. Pour le code qui est censé être lisible, il semble assez difficile de comprendre ce qui peut et devrait être dans la portée et quelle sera la valeur à un moment donné - je pense actuellement à passer du Python langue à cause de ces inconvénients.
Ce n'est pas parce que "nous sommes tous des adultes consentants" que nous ne faisons pas d'erreurs et ne travaillons pas mieux dans une structure solide, en particulier lorsque nous travaillons sur des projets complexes - l'indentation et les soulignements dénués de sens ne semblent pas être suffisants .
Mes reproches à propos de Python:
La répartition multiple ne s'intègre pas bien avec le système de type à répartition unique établi et n'est pas très performante.
Le chargement dynamique est un problème majeur sur les systèmes de fichiers parallèles où la sémantique de type POSIX entraîne des ralentissements catastrophiques pour les opérations gourmandes en métadonnées. J'ai des collègues qui ont brûlé un quart de million d'heures de base en obtenant Python (avec numpy, mpi4py, petsc4py et d'autres modules d'extension) chargés sur des cœurs de 65 000. (La simulation a fourni une nouvelle science importante résultats, donc cela en valait la peine, mais c'est un problème lorsque plus d'un baril de pétrole est brûlé pour charger Python une fois.) L'incapacité à établir un lien statique nous a obligés à faire de grandes contorsions pour obtenir des temps de chargement raisonnables à grande échelle, y compris patcher libc-rtld pour que dlopen
effectue un accès collectif au système de fichiers.
Quoi qu'il en soit, python est ma langue principale depuis 4 ans maintenant. Être fanboys, élitistes ou monomaniaques ne fait pas partie de la culture python.
Je préfère python et le premier inconvénient qui me vient à l'esprit est lorsque vous commentez une instruction comme if myTest():
alors vous devez changer l'indentation de tout le bloc exécuté que vous ne voudriez pas ça n'a rien à voir avec C ou Java. En fait dans python au lieu de commenter une clause if à la place, j'ai commencé à la commenter de cette façon: `if True: #myTest ( ) donc je n'aurai pas besoin de changer le bloc de code suivant. Puisque Java et C ne reposent pas sur l'indentation, il est plus facile de commenter les instructions avec C et Java.).
Mais il a de grandes fonctionnalités de rachat:
len(s)
à __len__(self)
et autres "méthodes spéciales"__add__
et __iadd__
pour +
et +=
)self
comme premier paramètre de méthode"Immuabilité" n'est pas exactement son point fort. Les nombres, les tuples et les chaînes AFAIK sont immuables, tout le reste (c'est-à-dire les objets) est modifiable. Comparez cela à des langages fonctionnels comme Erlang ou Haskell où tout est immuable (par défaut, au moins).
Cependant, l'immuabilité brille vraiment vraiment avec la concurrence *, qui n'est pas non plus le point fort de Python, donc au moins c'est conséquent.
(* = Pour les nitpickers: je veux dire la concurrence qui est au moins partiellement parallèle. Je suppose que Python est ok avec la concurrence "single-threaded", dans laquelle l'immuabilité n'est pas aussi importante. (Oui, Amateurs de FP, je sais que l'immuabilité est excellente même sans concurrence.))
J'aimerais avoir des constructions explicitement parallèles. Le plus souvent, lorsque j'écris une liste de compréhension comme
[ f(x) for x in lots_of_sx ]
Je me fiche de l'ordre dans lequel les éléments seront traités. Parfois, je me fiche même de l'ordre dans lequel ils sont retournés.
Même si CPython ne peut pas le faire correctement lorsque mon f est pur Python, un comportement comme celui-ci pourrait être défini pour d'autres implémentations à utiliser.
Python n'a pas d'optimisation d'appel final, principalement pour raisons philosophiques . Cela signifie que la récurrence de queue sur de grandes structures peut coûter de la mémoire O(n) (en raison de la pile inutile qui est conservée) et vous obligera à réécrire la récursivité en boucle pour obtenir O(1) mémoire.