64 votes

Les gotchas et les mines de Python 2.x

Le but de ma question est de renforcer ma base de connaissances avec Python et d'en avoir une meilleure image, ce qui inclut de connaître ses défauts et ses surprises. Pour rester précis, je ne m'intéresse qu'à l'interpréteur CPython.

Je cherche quelque chose de similaire à ce que j'ai appris de mon Les mines anti-papier où certaines des réponses étaient bien connues de moi, mais d'autres étaient à la limite de l'horreur.

Mise à jour : Apparemment, une ou deux personnes sont contrariées par le fait que j'ai posé une question à laquelle on a déjà partiellement répondu en dehors de Stack Overflow. Comme une sorte de compromis voici l'URL http://www.ferg.org/projects/python_gotchas.html

Notez qu'une ou deux réponses ici sont déjà originales par rapport à ce qui a été écrit sur le site référencé ci-dessus.

0 votes

Je ne suis pas certain que le passage de la version 2.5 à la version 2.6 comporte beaucoup de problèmes. Si votre intention est de parler de la série python 2.X en général, il serait peut-être préférable de changer le titre en 2.X.

0 votes

Quel était le problème avec la liste dans ferg.org/projets/python_gotchas.html ?

0 votes

@S. Lott - Il n'y a rien de mal à cela, c'est juste que je n'étais pas au courant et que personne n'a posé cette question dans SO.

85voto

Garth Kidd Points 2792

Les expressions dans les arguments par défaut sont calculées lorsque la fonction est définie, no quand il est appelé.

Exemple : envisager de mettre par défaut un argument à l'heure actuelle :

>>>import time
>>> def report(when=time.time()):
...     print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19

Le site when L'argument ne change pas. Il est évalué lorsque vous définissez la fonction. Il ne changera pas jusqu'à ce que l'application soit redémarrée.

Stratégie : vous ne trébucherez pas sur ce point si vous utilisez des arguments par défaut à None et ensuite faire quelque chose d'utile quand vous le voyez :

>>> def report(when=None):
...     if when is None:
...         when = time.time()
...     print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23

Exercice : pour s'assurer que vous avez bien compris : pourquoi cela se produit-il ?

>>> def spam(eggs=[]):
...     eggs.append("spam")
...     return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']

0 votes

+1 Excellent point ! En fait, je me suis appuyé sur cette méthode dans un contexte similaire, mais je pourrais facilement voir que cela prend les imprudents au dépourvu !

0 votes

C'est le piège le plus connu, mais je n'en ai jamais été victime avant de le savoir.

0 votes

Il en va de même pour les variables de niveau classe (une erreur facile à commettre lors de l'apprentissage de Python).

61voto

DzinX Points 13452

Vous devez savoir comment les variables de classe sont traitées en Python. Considérons la hiérarchie de classes suivante :

class AAA(object):
    x = 1

class BBB(AAA):
    pass

class CCC(AAA):
    pass

Maintenant, vérifiez la sortie du code suivant :

>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
>>> print AAA.x, BBB.x, CCC.x
3 2 3

Surpris ? Vous ne le serez pas si vous vous souvenez que les variables de classe sont gérées en interne comme des dictionnaires d'un objet de classe. Pour les opérations de lecture Si le nom d'une variable n'est pas trouvé dans le dictionnaire de la classe courante, il est recherché dans les classes parentes. Donc, encore le code suivant, mais avec des explications :

# AAA: {'x': 1}, BBB: {}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
# AAA: {'x': 1}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
# AAA: {'x': 3}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
3 2 3

Il en va de même pour la gestion des variables de classe dans les instances de classe (considérez cet exemple comme la suite de celui qui précède) :

>>> a = AAA()
# a: {}, AAA: {'x': 3}
>>> print a.x, AAA.x
3 3
>>> a.x = 4
# a: {'x': 4}, AAA: {'x': 3}
>>> print a.x, AAA.x
4 3

41voto

Richard Levasseur Points 5428

Boucles et lambdas (ou n'importe quelle fermeture, vraiment) : les variables sont liées par nom

funcs = []
for x in range(5):
  funcs.append(lambda: x)

[f() for f in funcs]
# output:
# 4 4 4 4 4

Une solution consiste à créer une fonction distincte ou à passer les arguments par leur nom :

funcs = []
for x in range(5):
  funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4

20voto

Algorias Points 1144

La liaison dynamique rend les fautes de frappe dans vos noms de variables étonnamment difficiles à trouver. Il est facile de passer une demi-heure à corriger un bogue insignifiant.

EDIT : un exemple...

for item in some_list:
    ... # lots of code
... # more code
for tiem in some_other_list:
    process(item) # oops!

1 votes

+1 Ouais, ça m'a fait foirer une ou deux fois, est-ce que tu pourrais donner un exemple dans ta réponse ?

3 votes

Je suppose que oui, mais c'était juste pour l'illustration. Les occurrences réelles de ce type de bogue ont tendance à être un peu plus complexes.

12 votes

Vous pouvez utiliser des vérificateurs statiques comme PyLint pour trouver ces erreurs -- tiem serait marqué comme une variable inutilisée.

18voto

Sven Marnach Points 133943

L'une des plus grandes surprises que j'ai eues avec Python est celle-ci :

a = ([42],)
a[0] += [43, 44]

Cela fonctionne comme on pourrait s'y attendre, sauf qu'une TypeError est levée après la mise à jour de la première entrée du tuple ! Ainsi, a sera ([42, 43, 44],) après avoir exécuté le += mais il y aura quand même une exception. Si vous essayez ceci d'un autre côté

a = ([42],)
b = a[0]
b += [43, 44]

vous n'obtiendrez pas d'erreur.

2 votes

Ou vous pouvez simplement écrire : a[0].extend([43, 44]) .

2 votes

Wow. Je considère que changer et lever une exception après coup est un bug en Python. Y a-t-il une raison pour que ce ne soit qu'une verrue ?

1 votes

WOW. Je m'attendais à l'erreur, mais je ne m'attendais pas à ce qu'elle soit vraiment modifier la liste aussi. C'est moche. Cependant, je ne pense pas avoir jamais rencontré ce problème car j'ai l'habitude de ne pas utiliser de tuples avec des données que je veux modifier. Même si elles pointent vers la même liste, je préfère que les valeurs restent immuables. Si je veux avoir des éléments positionnels qui peuvent changer, j'utilise soit une liste, un dictionnaire ou une classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X