5 votes

Les appels de fonction Python ont un champ d'application insuffisant, ils n'ont pas d'état et n'initialisent pas les paramètres ?

Avant d'avoir l'audace de déposer un rapport de bogue, j'ai pensé vérifier mes hypothèses parmi les Pythonistes plus sages ici. J'ai rencontré un cas déconcertant aujourd'hui, alors je l'ai réduit à un petit exemple, présenté ci-dessous :

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

"""
A little script to demonstrate that a function won't re-initialize its
list parameters between calls, but instead allows them to retain state.

"""

def bleedscope(a=[], b=[]):
    """
    On each call, unless explicitly passed, both `a` and `b` should be
    initialized as empty lists.

    """

    c = a
    if b:
        c.extend(b)
    return len(c)

x = bleedscope(b=[1])
print x     # Should be 1, as expected.
x = bleedscope(b=[2])
print x     # Expect also to be 1, but it's 2. `a` is retained.
x = bleedscope(a=[1])
print x     # Now 1 as expected.
x = bleedscope(b=[3])
print x     # 1 as expected? No, it's 3! Insanity!

Je pensais que les arguments de fonction étaient locaux dans la portée de la fonction, et qu'ils étaient ramassés à la fin d'un appel de fonction, sans jamais conserver d'état entre eux. J'ai testé le script ci-dessus sur Python 2.5.2 et Python 2.6.1, cependant, et ma compréhension ne les résultats. Argument a conserve certainement l'état entre la plupart de ces appels ; le plus perplexe étant l'appel final à bleedscope où il saute l'état de l'appel précédent et revient à l'état à la fin du deuxième appel (c'est-à-dire, [1, 2] ). [Je vous suggère de l'exécuter dans votre débogueur préféré pour le constater par vous-même. Si vous n'en avez pas, je suggère Winpdb comme un solide débogueur Python autonome FOSS].

Qu'est-ce qui se passe ici ?

15voto

Kai Points 4954

En Python, les valeurs des paramètres par défaut ne sont initialisées que lorsque l'appel def est analysé. Dans le cas d'un objet (comme vos listes), il est réutilisé entre les appels. Jetez un coup d'œil à cet article à ce sujet, qui fournit également la solution de contournement nécessaire :

http://effbot.org/zone/default-values.htm

8voto

Unknown Points 22789

C'est votre problème :

def bleedscope(a=[], b=[]):

il devrait l'être

def bleedscope(a=None, b=None):
    if a is None: a = []
    if b is None: b = []

Les paramètres par défaut ne sont exécutés qu'une seule fois lorsque la fonction est analysée, utilisant ainsi les 2 mêmes listes à chaque fois.

5voto

John Machin Points 39706

Il y a une explication dans le FAQ

1voto

GhiOm Points 13732

Il est amusant de constater que votre entrée et votre sortie sont assez similaires, pour des raisons totalement accidentelles.

En fait, ce qui se passe avec Python, c'est que les valeurs par défaut pour a et b dans votre déclaration de méthode sont des valeurs "statiques". Elles sont instanciées une fois lors de la définition de la méthode. Donc votre "a" par défaut est poussé chaque fois que vous ne passez pas un "a" comme argument.

Mettez un "print a" au début de votre méthode pour que cela se produise.

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