Molte risposte contengono piccole parti delle informazioni complete, quindi mi piacerebbe riunirle tutte insieme ai miei pattern preferiti.
il valore predefinito è un mutable
tipo
Se il valore predefinito è un oggetto modificabile, sei fortunato: puoi sfruttare il fatto che gli argomenti predefiniti di Python vengono valutati una volta quando la funzione è definita (un po 'di più su questo alla fine della risposta nell'ultima sezione)
Ciò significa che puoi facilmente confrontare un valore modificabile predefinito utilizzando is
per vedere se è stato passato come argomento o lasciato per impostazione predefinita, come negli esempi seguenti come funzione o metodo:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
e
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Argomenti predefiniti immutabili
Ora, è un po 'meno elegante se il tuo valore predefinito dovrebbe essere un immutable
valore (e ricorda che anche le stringhe sono immutabili!) Perché non puoi sfruttare il trucco così com'è, ma c'è ancora qualcosa che puoi fare, sfruttando ancora mutabile genere; fondamentalmente si inserisce un valore predefinito "falso" mutabile nella firma della funzione e il valore predefinito "reale" desiderato nel corpo della funzione.
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)
È particolarmente divertente se il valore predefinito reale è None
, ma None
è immutabile, quindi ... è comunque necessario utilizzare esplicitamente un mutabile come parametro predefinito della funzione e passare a Nessuno nel codice.
Utilizzo di una Default
classe per valori predefiniti immutabili
o, in modo simile al suggerimento di @cz, se i documenti di Python non sono sufficienti :-), puoi aggiungere un oggetto in mezzo per rendere l'API più esplicita (senza leggere i documenti); l'istanza della classe used_proxy_ Default è modificabile e conterrà il valore predefinito reale che si desidera utilizzare.
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))
adesso:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Quanto sopra funziona bene anche per Default(None)
.
Altri modelli
Ovviamente i modelli di cui sopra sembrano più brutti di quanto dovrebbero a causa di tutti quelli print
che sono lì solo per mostrare come funzionano. Altrimenti li trovo abbastanza concisi e ripetibili.
Potresti scrivere un decoratore per aggiungere il __call__
pattern suggerito da @dmg in un modo più semplificato, ma questo obbligherà comunque a usare strani trucchi nella definizione della funzione stessa: dovresti dividerlo value
e value_default
se il tuo codice ha bisogno di distinguerli, quindi Non vedo molti vantaggi e non scriverò l'esempio :-)
Tipi mutabili come valori predefiniti in Python
Qualcosa in più sul # 1 gotcha di Python! , abusato per il tuo piacere sopra. Puoi vedere cosa succede a causa della valutazione alla definizione facendo:
def testme(default=[]):
print(id(default))
Puoi eseguire testme()
tutte le volte che vuoi, vedrai sempre un riferimento alla stessa istanza predefinita (quindi in pratica il tuo valore predefinito è immutabile :-)).
Ricordate che in Python ci sono solo 3 mutabili tipi built-in : set
, list
, dict
; tutto il resto - anche gli archi! - è immutabile.