2010-09-02 15 views
10

Napisałem mały fragment, który oblicza się długość ścieżki danego węzła (np jego odległość do węzła głównego):Python: Dziwne zachowanie funkcji rekurencyjnej z argumentów kluczowych

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) 

Teraz jest coś dziwnego dzieje się z tą funkcją (przynajmniej jest to dla mnie dziwne): Wywołanie funkcji node_depth po raz pierwszy zwraca właściwą wartość. Jednak wywołanie go po raz drugi z tym samym węzłem zwraca -1. W colored_nodes zestaw jest pusty w pierwszym naborze, ale zawiera wszystkie node-ID w drugim naborze, które zostały dodane podczas pierwszego:

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 

jestem brakuje trochę Pythona konkretnej rzeczy tutaj i jest to naprawdę podobno Być w ten sposób?

Dzięki z góry,

Jena

+0

To samo mnie ugryzło i znalazłem twoje pytanie i inne TAK szczegółowe wyjaśnienia. http://stackoverflow.com/questions/1132941/least-astonishment-in-python-which-scope-is-the-mutable-default-argument-in – Yefei

Odpowiedz

15

„wartość domyślna” dla parametru funkcji w Pythonie jest tworzony w momencie deklaracji funkcji, a nie za każdym razem funkcja jest wywoływana. Rzadko chcesz zmutować domyślną wartość parametru, dlatego często dobrym pomysłem jest użycie czegoś niezmiennego dla wartości domyślnej.

W twoim przypadku może chcesz zrobić coś takiego:

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

To dobrze wiedzieć - wielkie dzięki. (A ponieważ ktoś zapytał: Nie, to nie jest praca domowa.) – jena

+0

Dzięki za to! Świetna rada. @jena: dzięki, że o to pytasz. :) – hayavuk

6

To jest, ponieważ w Pythonie, wartości argumentów domyślne nie oceniano za każdym razem, gdy funkcja jest wywoływana, ale tylko raz na czas definiowania funkcji. Tak skutecznie wywołujecie funkcję z wstępnie wypełnionym zestawem colored_nodes na każdym innym, ale pierwszym wywołaniu po definicji.

+0

Dzięki, nauczyłem się czegoś. Teraz mój dzień jest kompletny. – wheaties