Banyak jawaban memiliki potongan-potongan kecil dari info lengkap, jadi saya ingin menggabungkan semuanya dengan pola favorit saya.
nilai default adalah mutable
tipe
Jika nilai default adalah objek yang bisa berubah, Anda beruntung: Anda dapat mengeksploitasi fakta bahwa argumen default Python dievaluasi satu kali ketika fungsi didefinisikan (lebih banyak lagi tentang ini di akhir jawaban di bagian terakhir)
Ini berarti Anda dapat dengan mudah membandingkan nilai default yang dapat berubah menggunakan is
untuk melihat apakah nilai itu diteruskan sebagai argumen atau dibiarkan secara default, seperti dalam contoh berikut sebagai fungsi atau metode:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
dan
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Argumen default yang tidak dapat diubah
Sekarang, agak kurang elegan jika default Anda diharapkan menjadi immutable
nilai (dan ingat bahwa bahkan string tidak dapat diubah!) Karena Anda tidak dapat mengeksploitasi trik apa adanya, tetapi masih ada sesuatu yang dapat Anda lakukan, masih mengeksploitasi mutable Tipe; pada dasarnya Anda menempatkan default "palsu" yang bisa berubah di tanda tangan fungsi, dan nilai default "nyata" yang diinginkan di badan fungsi.
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)
Rasanya sangat lucu jika Anda benar-benar default None
, tetapi None
tetap jadi ... Anda masih perlu secara eksplisit menggunakan mutable sebagai parameter default fungsi, dan beralih ke None dalam kode.
Menggunakan Default
kelas untuk default yang tidak dapat diubah
atau, mirip dengan saran @cz, jika dokumen python tidak cukup :-), Anda dapat menambahkan objek di antaranya untuk membuat API lebih eksplisit (tanpa membaca dokumen); instance kelas used_proxy_ Default bisa berubah, dan akan berisi nilai default sebenarnya yang ingin Anda gunakan.
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))
sekarang:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Di atas bekerja dengan baik juga untuk Default(None)
.
Pola lainnya
Jelas sekali pola di atas terlihat lebih jelek dari yang seharusnya karena semua print
yang ada hanya untuk menunjukkan bagaimana mereka bekerja. Kalau tidak, saya menganggapnya singkat dan cukup berulang.
Anda dapat menulis dekorator untuk menambahkan __call__
pola yang disarankan oleh @dmg dengan cara yang lebih efisien, tetapi ini tetap mengharuskan penggunaan trik aneh dalam definisi fungsi itu sendiri - Anda perlu membagi value
dan value_default
jika kode Anda perlu membedakannya, jadi Saya tidak melihat banyak keuntungan dan saya tidak akan menulis contohnya :-)
Jenis yang dapat berubah sebagai nilai default dengan Python
Sedikit lebih banyak tentang # 1 python gotcha! , disalahgunakan untuk kesenangan Anda sendiri di atas. Anda dapat melihat apa yang terjadi karena evaluasi pada definisi dengan melakukan:
def testme(default=[]):
print(id(default))
Anda dapat menjalankan testme()
sebanyak yang Anda inginkan, Anda akan selalu melihat referensi ke instance default yang sama (jadi pada dasarnya default Anda tidak dapat diubah :-)).
Ingat bahwa dalam Python hanya ada 3 bisa berubah built-in jenis : set
, list
, dict
; yang lainnya - bahkan string! - tidak bisa diubah.