2010-09-02 19 views
10

J'ai écrit un petit extrait qui calcule la longueur du chemin d'un noeud donné (par exemple, la distance au nœud racine):Python: Comportement étrange de la fonction récursive avec des arguments de mot-clé

def node_depth(node, depth=0, colored_nodes=set()): 
    """ 
    Return the length of the path in the parse tree from C{node}'s position 
    up to the root node. Effectively tests if C{node} is inside a circle 
    and, if so, returns -1. 

    """ 
    if node.mother is None: 
     return depth 
    mother = node.mother 
    if mother.id in colored_nodes: 
     return -1 
    colored_nodes.add(node.id) 
    return node_depth(mother, depth + 1, colored_nodes) 

Maintenant, il y a un chose étrange qui se passe avec cette fonction (au moins c'est étrange pour moi): Appeler node_depth pour la première fois renvoie la bonne valeur. Toutefois, l'appeler une seconde fois avec le même nœud renvoie -1. Les colored_nodes ensemble est vide dans le premier appel, mais contient tous les noeuds-ID dans le second appel qui ont été ajoutés au cours de première:

print node_depth(node) # --> 9 
# initially colored nodes --> set([]) 
print node_depth(node) # --> -1 
# initially colored nodes --> set([1, 2, 3, 38, 39, 21, 22, 23, 24]) 

print node_depth(node, colored_nodes=set()) # --> 9 
print node_depth(node, colored_nodes=set()) # --> 9 

Suis-je manque quelque chose spécifique à Python ici et cela est vraiment censé Être comme ça?

Merci à l'avance,

Jena

+0

La même chose mordre moi et j'ai trouvé votre question et une autre explication de SO de détail. http://stackoverflow.com/questions/1132941/least-astonishment-in-python-which-scope-is-the-mutable-default-argument-in – Yefei

Répondre

15

La « valeur par défaut » pour un paramètre de fonction en Python est instancié au moment de la déclaration de fonction, pas à chaque fois la fonction est appelée. Vous avez rarement envie de muter la valeur par défaut d'un paramètre, et c'est donc souvent une bonne idée d'utiliser quelque chose d'immuable pour la valeur par défaut.

Dans votre cas, vous voudrez peut-être faire quelque chose comme ceci:

def node_depth(node, depth=0, colored_nodes=None): 
    ... 
    if colored_nodes is None: colored_nodes = set() 
+0

C'est une bonne chose à savoir - merci beaucoup. (Et comme quelqu'un a demandé: Non, ce n'est pas un devoir.) – jena

+0

Merci pour ça! Bon conseil. @jena: merci d'avoir demandé ça aussi. :) – hayavuk

6

Ceci est, car en Python, les valeurs des arguments par défaut sont pas évalué chaque fois que la fonction est appelée, mais une seule fois à temps de définition de la fonction. Donc, effectivement, vous appelez la fonction avec un colored_nodes pré-rempli sur tous les appels sauf le premier appel après définition.

+0

Merci, j'ai appris quelque chose. Maintenant, ma journée est terminée. – wheaties