Każdy, kto majstruje przy Pythonie wystarczająco długo, został ugryziony (lub rozdarty na kawałki) przez następujący problem:
def foo(a=[]):
a.append(5)
return a
Nowicjusze Python oczekiwałby to funkcja zawsze zwraca listę z tylko jednego elementu: [5]. Rezultat jest natomiast zupełnie inny i bardzo zadziwiający (dla nowicjusza):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Mój menedżer po raz pierwszy spotkał się z tą funkcją i nazwał ją „dramatyczną wadą projektową” języka. Odpowiedziałem, że zachowanie ma podstawowe wytłumaczenie i jest naprawdę bardzo zagadkowe i nieoczekiwane, jeśli nie rozumiesz elementów wewnętrznych. Nie byłem jednak w stanie odpowiedzieć (sobie) na następujące pytanie: jaki jest powód wiązania domyślnego argumentu przy definicji funkcji, a nie przy wykonywaniu funkcji? Wątpię, czy doświadczone zachowanie ma praktyczne zastosowanie (kto tak naprawdę używał zmiennych statycznych w C, bez powodowania błędów?)
Edytuj :
Baczek dał ciekawy przykład. Wraz z większością twoich komentarzy, w szczególności z Utaal, rozwinąłem dalej:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Wydaje mi się, że decyzja projektowa była związana z tym, gdzie umieścić zakres parametrów: wewnątrz funkcji czy „razem z nią”?
Wykonanie wiązania wewnątrz funkcji oznaczałoby, że xjest skutecznie powiązane z określonym domyślnym, gdy funkcja jest wywoływana, nieokreślona, co może mieć głęboką wadę: deflinia byłaby „hybrydowa” w tym sensie, że część wiązania ( obiekt funkcji) miałby miejsce w momencie definicji, a część (przypisanie parametrów domyślnych) w czasie wywołania funkcji.
Rzeczywiste zachowanie jest bardziej spójne: wszystko tej linii jest oceniane podczas wykonywania tej linii, co oznacza przy definicji funkcji.
[5]” Jestem początkującym Python i nie spodziewałbym się tego, bo oczywiście foo([1])wróci [1, 5], nie [5]. To, co chciałeś powiedzieć, to to, że nowicjusz oczekiwałby, że funkcja wywoływana bez parametru zawsze będzie zwracać [5].