Jaki jest zakres domyślnego parametru w Pythonie?

Kiedy definiujesz funkcję w Pythonie z parametrem array, jaki jest zakres tego parametru?

Ten przykład pochodzi z samouczka Pythona:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Druki:

[1]
[1, 2]
[1, 2, 3]
Nie jestem pewien, czy rozumiem, co tu się dzieje. Czy to oznacza, że zakres tablicy jest poza funkcją? Dlaczego tablica zapamiętuje swoje wartości od wywołania do wywołania? PochodzÄ ... c z innych jÄ ™ zykĂłw, spodziewaĺ ' bym siÄ ™ takiego zachowania tylko wtedy, gdy zmienna jest statyczna. Inaczej wygląda na to, że powinien być resetowany za każdym razem. A właściwie, kiedy próbowałem:
def f(a):
    L = []
    L.append(a)
    return L

Otrzymałem zachowanie, którego oczekiwałem(tablica była resetowana przy każdym wywołaniu).

Wydaje mi się więc, że potrzebuję tylko wyjaśnienia linii def f(a, L=[]): - jaki jest zakres zmiennej L?

Author: charlie, 2010-02-25

7 answers

Zakres jest taki, jak można się spodziewać.

Być może zaskakujące jest to, że wartość domyślna jest obliczana tylko raz i używana ponownie, więc za każdym razem, gdy wywołujesz funkcję, otrzymujesz tę samą listę, a nie nową listę zainicjowaną na [].

Lista jest przechowywana w f.func_defaults.

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)
print f.func_defaults
f.func_defaults = (['foo'],) # Don't do this!
print f(4)

Wynik:

[1]
[1, 2]
[1, 2, 3]
([1, 2, 3],)
['foo', 4]
 22
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 15:52:58

Zakres zmiennej L zachowuje się zgodnie z oczekiwaniami.

"problem" jest z listą, którą tworzysz []. Python nie tworzy nowej listy za każdym razem, gdy wywołujesz funkcję. L otrzymuje tę samą listę za każdym razem, gdy wywołujesz, dlatego funkcja "zapamiętuje" poprzednie wywołania.

Więc w efekcie to jest to, co masz:

mylist = []
def f(a, L=mylist):
    L.append(a)
    return L

Tutorial Python przedstawia to w ten sposób :

Wartość domyślna jest obliczana tylko raz. To robi różnicę, gdy domyślnym obiektem jest obiekt zmienny, taki jak lista, słownik lub instancje większości klas.

I sugeruje następujący sposób kodowania oczekiwanego zachowania:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L
 5
Author: Dave Webb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 15:34:37

Jest jeszcze mniej "magii", niż można by przypuszczać. Jest to równoważne

m = []

def f(a, L=m):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

m jest tworzony tylko raz.

 3
Author: Joe Koberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 15:30:47

Powiedz, że masz następujący kod:

def func(a=[]):
    a.append(1)
    print("A:", a)

func()
func()
func()

Możesz użyć wcięć Pythona, aby pomóc ci zrozumieć, co się dzieje. Wszystko, co jest spłukane do lewego marginesu jest wykonywane, gdy plik zostanie wykonany. Wszystko, co jest wcięte, jest kompilowane do obiektu kodu, który jest wykonywany po wywołaniu func(). Tak więc funkcja jest zdefiniowana, a jej domyślne argumenty ustawiane raz, gdy program zostanie uruchomiony (ponieważ instrukcja def jest flush left).

Co robi z domyślne argumenty to interesujący problem. W Pythonie 3 większość informacji o funkcji umieszcza w dwóch miejscach: func.__code__ i func.__defaults__. W Pythonie 2, func.__code__ był func.func_code func.__defaults__ was func.func_defaults. Późniejsze wersje Pythona 2, w tym 2.6, mają oba zestawy nazw, aby ułatwić przejście z Pythona 2 do Pythona 3. Użyję bardziej nowoczesnych __code__ i __defaults__. Jeśli utkniesz na starszym Pythonie, pojęcia są takie same; różnią się tylko nazwy.

Wartości domyślne są przechowywane w func.__defaults__, oraz pobierane za każdym razem, gdy funkcja jest wywoływana.

Tak więc, gdy zdefiniujesz funkcję powyżej, ciało funkcji zostanie skompilowane i zapisane w zmiennych w __code__, które zostaną wykonane później, a domyślne argumenty zostaną zapisane w __defaults__. Gdy wywołujesz funkcję, używa ona wartości w __defaults__. Jeśli te wartości zostaną zmodyfikowane z jakiegokolwiek powodu, to tylko zmodyfikowana wersja jest dostępna do użycia.

Baw się definiowaniem różnych funkcji w interaktywnym interpreterze i zobacz co możesz dowiedzieć się o tym, jak python tworzy i używa funkcji.

 1
Author: jcdyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 16:31:39

"problem" polega na tym, że L=[] jest oceniany tylko raz , czyli podczas kompilacji pliku. Python przechodzi przez każdą linię pliku i kompiluje go. Zanim osiągnie def z domyślnym parametrem, utworzy tę instancję listy raz.

Jeśli umieścisz L = [] wewnątrz kodu funkcji, instancja nie zostanie utworzona w "czasie kompilacji" (w rzeczywistości czas kompilacji może być również nazywany częścią czasu wykonania), ponieważ Python kompiluje kod funkcji, ale go nie wywołuje. Otrzymasz więc nową instancję listy , ponieważ tworzenie odbywa się za każdym razem, gdy wywołasz funkcję (zamiast raz podczas kompilacji).

Rozwiązaniem tego problemu nie jest używanie zmiennych obiektów jako domyślnych parametrów, lub tylko stałych instancji, takich jak None:

def f(a, L = None):
    if l == None:
        l = []
    L.append(a)
    return L

Zauważ, że w obu opisanych przez Ciebie przypadkach zakres L jest zakresem funkcji.

 0
Author: AndiDog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 15:45:31

Wyjaśnienie znajduje się w odpowiedziach na to pytanie . Podsumowując to tutaj:

Funkcje w Pythonie są rodzajem obiektu. Ponieważ są one rodzajem obiektu, zachowują się jak obiekty, gdy są utworzone. Funkcja, jeśli jest zdefiniowana jako domyślny argument z atrybutem zmiennym, jest dokładnie taka sama jak Klasa ze statycznym atrybutem, który jest zmienną listą.

Lennart Regebro ma dobre wyjaśnienie i odpowiedź na pytanie Roberto Liffredo jest doskonale.

Aby dostosować odpowiedź Lennarta ... if I have a BananaBunch class:

class BananaBunch:
    bananas = []

    def addBanana(self, banana):
        self.bananas.append(banana)


bunch = BananaBunch()
>>> bunch
<__main__.BananaBunch instance at 0x011A7FA8>
>>> bunch.addBanana(1)
>>> bunch.bananas
[1]
>>> for i in range(6):
    bunch.addBanana("Banana #" + i)
>>> for i in range(6):
    bunch.addBanana("Banana #" + str(i))

>>> bunch.bananas
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5']

// And for review ... 
//If I then add something to the BananaBunch class ...
>>> BananaBunch.bananas.append("A mutated banana")

//My own bunch is suddenly corrupted. :-)
>>> bunch.bananas
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5', 'A mutated banana']

Jak to ma zastosowanie do funkcji? funkcje w Pythonie są obiektami . To się powtarza. funkcje w Pythonie są obiektamis.

Więc kiedy tworzysz funkcję, tworzysz obiekt. Kiedy nadajesz funkcji zmienną wartość domyślną, wypełniasz atrybut tego obiektu zmienną wartością i za każdym razem, gdy wywołujesz tę funkcję, na której pracujesz ten sam atrybut. Jeśli więc używasz mutowalnego wywołania (np. append), modyfikujesz ten sam obiekt, tak jak gdybyś dodawał banany do obiektu bunch.

 0
Author: Sean Vieira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:00:10

Należy pamiętać, że python jest językiem interpretowanym. To, co się tutaj dzieje, to gdy funkcja " f "jest zdefiniowana, tworzy listę i przypisuje ją do domyślnego parametru" L "funkcji " f". Później, po wywołaniu tej funkcji, Ta sama lista jest używana jako domyślny parametr. Krótko mówiąc, kod z linii " def " jest wykonywany tylko raz, gdy funkcja jest zdefiniowana. Jest to częsta pułapka pytona, z której sam wpadłem.

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Były propozycje idiomów w innych odpowiedziach tutaj, aby rozwiązać ten problem. Ten, który proponowałbym jest następujący:

def f(a, L=None):
    L = L or []
    L.append(a)
    return L

To używa zwarcia or, aby wziąć "L", które zostało przekazane, lub utworzyć nową listę.

Odpowiedź na twoje pytanie to" L "ma tylko zakres w funkcji "f" , ale ponieważ domyślne parametry są przypisywane tylko raz do pojedynczej listy, a nie za każdym razem, gdy wywołujesz funkcję, zachowuje się tak, jakby domyślny parametr " L " miał globalny zakres.

 -1
Author: manifest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 15:52:50