Viele Antworten enthalten kleine Teile der vollständigen Informationen, daher möchte ich alles mit meinen Lieblingsmustern zusammenbringen.
Standardwert ist ein mutable
Typ
Wenn der Standardwert ein veränderbares Objekt ist, haben Sie Glück: Sie können die Tatsache ausnutzen, dass die Standardargumente von Python bei der Definition der Funktion einmal ausgewertet werden (mehr dazu am Ende der Antwort im letzten Abschnitt).
Dies bedeutet, dass Sie einen veränderlichen Standardwert leicht vergleichen können, indem Sie feststellen is
, ob er als Argument übergeben oder standardmäßig belassen wurde, wie in den folgenden Beispielen als Funktion oder Methode:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
und
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Unveränderliche Standardargumente
Jetzt ist es etwas weniger elegant, wenn erwartet wird, dass Ihre Standardeinstellung ein immutable
Wert ist (und denken Sie daran, dass sogar Zeichenfolgen unveränderlich sind!), Weil Sie den Trick nicht so ausnutzen können, wie er ist, aber es gibt immer noch etwas, das Sie tun können, indem Sie immer noch veränderlich ausnutzen Art; Grundsätzlich setzen Sie einen veränderlichen "falschen" Standard in die Funktionssignatur und den gewünschten "echten" Standardwert in den Funktionskörper.
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)
Es fühlt sich besonders lustig an, wenn Sie eine echte Standardeinstellung haben None
, diese aber None
unveränderlich ist. Sie müssen also weiterhin explizit eine veränderbare Variable als Standardparameter für die Funktion verwenden und im Code zu Keine wechseln.
Verwenden einer Default
Klasse für unveränderliche Standardeinstellungen
oder, ähnlich wie bei einem @ cz-Vorschlag, wenn Python-Dokumente nicht ausreichen :-), können Sie ein Objekt dazwischen hinzufügen, um die API expliziter zu gestalten (ohne die Dokumente zu lesen); Die Klasseninstanz used_proxy_ Default ist veränderbar und enthält den tatsächlichen Standardwert, den Sie verwenden möchten.
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))
jetzt:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Das obige funktioniert auch gut für Default(None)
.
Andere Muster
Offensichtlich sehen die obigen Muster hässlicher aus als sie sollten, weil all print
diese nur dazu dienen, zu zeigen, wie sie funktionieren. Ansonsten finde ich sie knapp und wiederholbar genug.
Sie könnten einen Dekorateur schreiben, um das __call__
von @dmg vorgeschlagene Muster rationaler hinzuzufügen , aber dies zwingt immer noch dazu, seltsame Tricks in der Funktionsdefinition selbst zu verwenden - Sie müssten sich aufteilen value
und value_default
wenn Ihr Code sie unterscheiden muss, so Ich sehe keinen großen Vorteil und werde das Beispiel nicht schreiben :-)
Veränderbare Typen als Standardwerte in Python
Ein bisschen mehr über # 1 Python Gotcha! , zu Ihrem eigenen Vergnügen oben missbraucht. Sie können sehen, was aufgrund der Bewertung bei der Definition passiert, indem Sie Folgendes tun:
def testme(default=[]):
print(id(default))
Sie können so oft ausführen, testme()
wie Sie möchten. Es wird immer ein Verweis auf dieselbe Standardinstanz angezeigt (Ihre Standardeinstellung ist also im Grunde unveränderlich :-)).
Denken Sie daran , dass in Python gibt es nur 3 wandelbar eingebauten Typen : set
, list
, dict
; alles andere - sogar Saiten! - ist unveränderlich.