Menambahkan nilai parameter default dengan petunjuk jenis di Python

236

Jika saya memiliki fungsi seperti ini:

def foo(name, opts={}):
  pass

Dan saya ingin menambahkan petunjuk tipe ke parameter, bagaimana saya melakukannya? Cara saya berasumsi memberi saya kesalahan sintaks:

def foo(name: str, opts={}: dict) -> str:
  pass

Berikut ini tidak menimbulkan kesalahan sintaksis tetapi sepertinya bukan cara intuitif untuk menangani kasus ini:

def foo(name: str, opts: dict={}) -> str:
  pass

Saya tidak dapat menemukan apa pun di typingdokumentasi atau pencarian Google.

Sunting: Saya tidak tahu bagaimana argumen default bekerja di Python, tetapi demi pertanyaan ini, saya akan menyimpan contoh di atas. Secara umum jauh lebih baik untuk melakukan hal berikut:

def foo(name: str, opts: dict=None) -> str:
  if not opts:
    opts={}
  pass
Astaga
sumber
4
Fungsi terakhir adalah cara yang benar. Sama scalahalnya dengan bahasa.
Israel Unterman
14
Anda memiliki tipe default yang bisa berubah - yang akan menyebabkan masalah
noɥʇʎԀʎzɐɹƆ
lihat jawaban pembaruan saya, @josh
noɥʇʎԀʎzɐɹƆ
@ noɥʇʎԀʎzɐɹƆ Tidak kecuali Anda menggunakannya untuk, misalnya memoisasi. : P
Mateen Ulhaq

Jawaban:

281

Cara kedua Anda benar.

def foo(opts: dict = {}):
    pass

print(foo.__annotations__)

output ini

{'opts': <class 'dict'>}

Memang benar bahwa itu tidak tercantum dalam PEP 484 , tetapi petunjuk jenis adalah aplikasi anotasi fungsi, yang didokumentasikan dalam PEP 3107. Bagian sintaksis memperjelas bahwa argumen kata kunci bekerja dengan anotasi fungsi dengan cara ini.

Saya sangat menyarankan agar tidak menggunakan argumen kata kunci yang dapat diubah. Informasi lebih lanjut di sini .

noɥʇʎԀʎzɐɹƆ
sumber
1
Lihat legacy.python.org/dev/peps/pep-3107/#syntax . Mengisyaratkan tipe hanyalah aplikasi anotasi fungsi.
chepner
@ partner benar. tidak tahu bahwa PEP 3107 memiliki sesuatu tentang argumen kata kunci.
noɥʇʎԀʎzɐɹƆ
2
Wow, saya tidak tahu tentang argumen default yang bisa diubah dalam Python ... terutama yang berasal dari Javascript / Ruby di mana argumen default bekerja secara berbeda. Tidak akan mengulangi apa yang sudah dikatakan ad mual sekitar SO tentang hal itu, saya hanya senang mengetahui hal ini sebelum itu menggigit saya. Terima kasih!
josh
6
Saya selalu disarankan untuk menggunakan None daripada tipe yang bisa berubah seperti {} atau [] atau objek default karena mutasi ke objek itu tanpa salinan yang dalam akan bertahan di antara iterasi.
MrMesees
2
tentukan fungsi yang cukup dengan argumen kata kunci yang dapat diubah dan hanya masalah waktu sebelum Anda melihat kembali pada sesi debugging 4 jam mempertanyakan pilihan hidup Anda
Joseph Sheedy
20

Jika Anda menggunakan mengetik (diperkenalkan di Python 3.5) Anda dapat menggunakan typing.Optional, di mana Optional[X]setara dengan Union[X, None]. Ini digunakan untuk memberi sinyal bahwa nilai eksplisit Nonediperbolehkan. Dari mengetik. Opsional :

def foo(arg: Optional[int] = None) -> None:
    ...
Tomasz Bartkowiak
sumber
2
Tidakkah seharusnya tidak ada spasi putih =di Optional[int] = Nonedalamnya seperti itu adalah konvensi untuk argumen kata kunci non-type-hinted?
actual_panda
7

Saya baru-baru ini melihat ini satu-liner:

def foo(name: str, opts: dict=None) -> str:
    opts = {} if not opts else opts
    pass
Kirkalicious
sumber
Hai @Kalalicious, terima kasih atas jawaban Anda. Bisakah Anda menjelaskan cara kerjanya?
Nathan
1
Dict kosong yang dilewatkan sebagai parameter default adalah dict yang sama untuk setiap panggilan. Jadi, jika fungsi bermutasi maka default waktu berikutnya akan menjadi nilai termutasi dari terakhir kali. Membuat Nonedefault dan kemudian memeriksa di dalam metode menghindari masalah ini dengan mengalokasikan dict baru setiap kali metode dipanggil.
Ian Goldby