2012-03-02 14 views
46

Mam funkcję Python, która pobiera listę jako parametr. Jeżeli ustawić domyślną wartość parametru do pustej listy jak ten:Najlepsza praktyka do ustawiania domyślnej wartości parametru, który powinien być listą w Pythonie?

def func(items=[]): 
    print items 

pylint chciał mi powiedzieć „Dangerous wartość domyślna [] jako argumentu”. Zastanawiałem się więc, jaka jest tutaj najlepsza praktyka?

+3

to jest coś, co każdy początkujący użytkownik Pythona jednoczy się raz lub dwa razy, jest całkiem fajne, że pylint powstrzymuje cię przed napisaniem okropnego błędu! – wim

Odpowiedz

64

Zastosowanie None jako wartość domyślna:

def func(items=None): 
    if items is None: 
     items = [] 
    print items 

Problem z zmienny domyślnym argumentem jest to, że zostanie on podzielony między wszystkich wywołań funkcji - patrz „Ważne ostrzeżenie” w relevant section of the Python tutorial.

+2

Ah wow, jestem zaskoczony, że ludzie o tym wiedzą. =) Nienawidziłem tej funkcji Pythona tak bardzo, że napisałem własną bibliotekę dekoratorów, aby umożliwić 'func (items = new ([]))' składnię. – ninjagecko

+0

Dzięki! Wygląda na to, że Python uważa, że ​​None jest zupełnie innym typem niż lista, więc zastanawiałem się, czy w Pythonie jest ogólnie dopuszczalne ustawianie domyślnej wartości parametru na inny typ (na przykład Brak) (Wiem, że Python jest typem bez typu język, ale pochodzę z C++ ... lol)? –

+0

Jest to zupełnie coś innego niż mocno napisany język, taki jak C++. Po prostu myśl o zmiennych jako nazwach, które odwołują się do obiektów, a wszystko jest przekazywane przez odniesienie. – wim

2

Dla obiektu zmiennego jako parametru domyślnego w deklaracjach funkcji i metod problem polega na tym, że ocena i tworzenie odbywa się dokładnie w tym samym momencie. Python-analizator odczytuje nagłówek funkcji i ocenia go w tym samym momencie.

Większość początkujących zakłada, że ​​nowy obiekt jest tworzony przy każdym połączeniu, ale to nie jest poprawne! JEDEN obiekt (w twoim przykładzie lista) tworzony jest w momencie DEKLARACJI, a nie na żądanie, gdy wywołujesz metodę.

W przypadku obiektów, które nie stanowią problemu, ponieważ wszystkie połączenia mają ten sam obiekt, jest on niezmienny i dlatego jego właściwości pozostają niezmienione.

Jako konwencja domyślnie używany jest obiekt None, aby wskazać użycie domyślnej inicjalizacji, która teraz może mieć miejsce w treści funkcji, która naturalnie jest oceniana w czasie połączenia.

0

W dodatku, a także lepiej zrozumieć, co pyton jest tu mój mały urywek tematyce:

from functools import wraps 
def defaultFactories(func): 
    'wraps function to use factories instead of values for defaults in call' 
    defaults = func.func_defaults 
    @wraps(func) 
    def wrapped(*args,**kwargs): 
     func.func_defaults = tuple(default() for default in defaults) 
     return func(*args,**kwargs) 
    return wrapped 

def f1(n,b = []): 
    b.append(n) 
    if n == 1: return b 
    else: return f1(n-1) + b 

@defaultFactories 
def f2(n,b = list): 
    b.append(n) 
    if n == 1: return b 
    else: return f2(n-1) + b 

>>> f1(6) 
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1] 
>>> f2(6) 
[1, 2, 3, 4, 5, 6] 
4

Właśnie spotkałem to po raz pierwszy, a moja natychmiastowa myśl jest „dobrze, nie chcę mimo to mutować listę, więc naprawdę chcę mieć domyślną listę niezmienną, więc Python da mi błąd, jeśli przypadkowo ją zmutuję. " Niezmienna lista to tylko krotka. Więc:

 
    def func(items=()): 
     print items 

Oczywiście, jeśli przekazać go do czegoś, co naprawdę chce listę (np isinstance (pozycje, listy)), to będzie cię w kłopoty. Ale i tak jest to zapach kodu.

+1

A jeśli wewnątrz funkcji musisz wykonać kopię, użyj 'my_copy = list (items)'. Wymyśliłeś proste i bardzo sprytne rozwiązanie typowego problemu. –