Wiele odpowiedzi zawiera małe fragmenty pełnych informacji, więc chciałbym zebrać to wszystko razem z moimi ulubionymi wzorami.
wartością domyślną jest mutable
typ
Jeśli wartością domyślną jest obiekt zmienny, masz szczęście: możesz wykorzystać fakt, że domyślne argumenty Pythona są oceniane tylko raz, gdy funkcja jest zdefiniowana (więcej na ten temat na końcu odpowiedzi w ostatniej sekcji)
Oznacza to, że możesz łatwo porównać domyślną zmienną wartość za pomocą, is
aby sprawdzić, czy została przekazana jako argument, czy pozostawiona domyślnie, jak w poniższych przykładach jako funkcja lub metoda:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
i
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Niezmienne argumenty domyślne
Teraz jest trochę mniej elegancko, jeśli domyślna wartość ma być immutable
wartością (i pamiętaj, że nawet łańcuchy są niezmienne!), Ponieważ nie możesz wykorzystać tej sztuczki w obecnej postaci, ale wciąż jest coś, co możesz zrobić, wciąż wykorzystując zmienne rodzaj; zasadniczo umieszczasz zmienną „fałszywą” wartość domyślną w sygnaturze funkcji, a żądaną „rzeczywistą” wartość domyślną w treści funkcji.
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
Wydaje się to szczególnie zabawne, jeśli prawdziwa wartość domyślna jest None
, ale None
jest niezmienna, więc ... nadal musisz jawnie użyć mutable jako domyślnego parametru funkcji i przełączyć się na None w kodzie.
Używanie Default
klasy dla niezmiennych wartości domyślnych
lub, podobnie jak w przypadku sugestii @cz, jeśli dokumentacja Pythona nie wystarcza :-), możesz dodać obiekt pomiędzy nimi, aby API było bardziej przejrzyste (bez czytania dokumentacji); instancja klasy used_proxy_ Default jest zmienna i będzie zawierała rzeczywistą wartość domyślną, której chcesz użyć.
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
teraz:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Powyższe działa ładnie również w przypadku Default(None)
.
Inne wzory
Oczywiście powyższe wzorce wyglądają brzydiej niż powinny, ponieważ wszystkie print
są tylko po to, aby pokazać, jak działają. W przeciwnym razie uważam je za zwięzłe i wystarczająco powtarzalne.
Mógłbyś napisać dekorator, który doda __call__
wzorzec sugerowany przez @dmg w bardziej uproszczony sposób, ale nadal będzie to obligowało do stosowania dziwnych sztuczek w samej definicji funkcji - musiałbyś się rozdzielić value
i value_default
jeśli twój kod musi je rozróżnić, więc Nie widzę dużej przewagi i nie będę pisać przykładu :-)
Zmienne typy jako wartości domyślne w Pythonie
Trochę więcej o # 1 python gotcha! , nadużywane powyżej dla własnej przyjemności. Możesz zobaczyć, co dzieje się w wyniku oceny na definicji , wykonując:
def testme(default=[]):
print(id(default))
Możesz uruchomić testme()
tyle razy, ile chcesz, zawsze zobaczysz odniesienie do tej samej domyślnej instancji (więc w zasadzie twoja domyślna jest niezmienna :-)).
Pamiętaj, że w Pythonie istnieją tylko 3 zmienne wbudowane typy : set
, list
, dict
; wszystko inne - nawet struny! - jest niezmienna.