Keyword-only dan positional-only

Penanda positional-only dan keyword-only untuk desain antarmuka fungsi yang tangguh.

Daftar isi

Python 3.8 melengkapi bahasa dengan penanda / untuk parameter positional-only dan penanda * untuk parameter keyword-only. Keduanya mengunci cara sebuah parameter boleh diisi, sehingga antarmuka fungsi tidak berubah apabila implementasinya disunting. Topik ini membahas kedua penanda, urutan lengkap parameter, serta alasannya dipakai dalam desain fungsi.

Parameter keyword-only#

Parameter yang ditulis setelah tanda * bersifat keyword-only, yaitu wajib disebut dengan namanya dan tidak dapat diisi secara posisi. Tanda * boleh diikuti *args, atau berdiri sendiri tanpa mengumpulkan apa pun. Parameter keyword-only yang diberi nilai default menjadi opsional.

python Contoh: parameter keyword-only
def buat_akun(nama, *, verifikasi=False):
    status = "terverifikasi" if verifikasi else "menunggu"
    print(nama, "-", status)

buat_akun("Ali")
buat_akun("Budi", verifikasi=True)
text Keluaran
Ali - menunggu
Budi - terverifikasi

Pemanggilan pertama hanya mengisi nama, sehingga verifikasi memakai defaultnya. Pemanggilan kedua menyebut verifikasi dengan namanya, dan itulah satu-satunya cara mengisinya. Pemanggilan yang mencoba mengisinya secara posisi ditolak oleh interpreter.

python Contoh: keyword-only diisi secara posisi
def buat_akun(nama, *, verifikasi=False):
    status = "terverifikasi" if verifikasi else "menunggu"
    print(nama, "-", status)

try:
    buat_akun("Budi", True)
except TypeError as e:
    print(type(e).__name__, str(e))
text Keluaran
TypeError buat_akun() takes 1 positional argument but 2 were given

Pesan error menyebut jumlah argumen posisi yang diterima, bukan nama parameter yang salah, karena verifikasi memang tidak menerima posisi sama sekali. Desain semacam ini mencegah pemanggil mengirim nilai pada posisi yang urutannya mungkin berubah di masa depan.

Tanda * yang berdiri sendiri tidak menambah parameter apa pun, dan itulah bedanya dengan *args. Bentuk itu dipakai semata untuk mengunci cara pengisian parameter di belakangnya. Aturan yang sama berlaku bila fungsinya juga memuat *args, karena penanda keyword-only mengikuti kumpulan argumen posisinya.

Parameter positional-only#

Parameter yang ditulis sebelum tanda / bersifat positional-only, yaitu wajib diisi secara posisi dan ditolak apabila disebut dengan nama. Penanda / harus berada sebelum parameter apa pun yang masih boleh disebut dengan nama.

python Contoh: parameter positional-only
def pangkat(basis, /, eksponen=2):
    return basis ** eksponen

print(pangkat(3))
print(pangkat(2, 5))
print(pangkat(3, eksponen=3))
text Keluaran
9
32
27

Tiga pemanggilan di atas sah karena basis selalu diisi secara posisi, sedangkan eksponen berada di kanan / sehingga boleh diisi posisi maupun nama. Nilai defaultnya tetap bekerja seperti parameter biasa.

python Contoh: positional-only disebut dengan nama
def pangkat(basis, /, eksponen=2):
    return basis ** eksponen

try:
    print(pangkat(basis=3))
except TypeError as e:
    print(type(e).__name__, str(e))
text Keluaran
TypeError pangkat() got some positional-only arguments passed as keyword arguments: 'basis'

Nama basis tidak diakui sebagai kata kunci karena parameternya positional-only. Nama semacam ini bebas disunting tanpa mematahkan pemanggil yang sudah ada, karena tidak ada pemanggil yang bergantung padanya.

Banyak fungsi bawaan memakai pola ini sebelum penandanya resmi ada, sehingga penanda / juga berperan menuliskan kontrak yang sebelumnya tidak tertulis. Penanda itu resmi menjadi bagian sintaks bahasa pada Python 3.8, dan sejak itu kontrak serupa dapat dinyatakan pada fungsi buatan sendiri.

Urutan lengkap parameter#

Semua bentuk parameter dapat digabung dengan urutan tetap, yaitu parameter positional-only, tanda /, parameter biasa, tanda * atau *args, parameter keyword-only, dan terakhir **kwargs. Diagram berikut merangkum urutan tersebut.

urutan penulisan parametera, /posisi sajab, cposisi/kata kunci*argsposisi variadikd, ekata kunci saja**kwargskata kunci variadikdef f(a, /, b, c, *args, d, e, **kwargs)Dari kiri ke kanan, cara pengisian berpindah dari posisi ke kata kunci.
Urutan parameter pada sebuah def
python Contoh: kelima bentuk parameter dalam satu def
def demo(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

demo(1, 2, c=3, d=4, e=5, f=6)
demo(1, 2, 3, d=4, e=5, f=6)
text Keluaran
1 2 3 4 5 6
1 2 3 4 5 6

Parameter a dan b hanya menerima posisi, c dan d menerima keduanya, sedangkan e dan f wajib disebut dengan nama. Dua pemanggilan di atas sah karena keduanya mengisi a sampai d secara posisi, lalu menyebut e dan f dengan nama. Tanda / dan * tidak muncul pada keluaran karena keduanya hanyalah penanda definisi.

Mengapa batasan cara pengisian dipakai#

Penanda ini memindahkan keputusan desain dari kebiasaan menjadi kontrak yang diperiksa interpreter. Banyak fungsi bawaan Python memakainya, misalnya len() yang parameter utamanya tidak menerima penyebutan nama, sehingga bentuk pemanggilan tertentu ditolak dengan jelas alih-alih dibiarkan. Kontrak tersebut menjelaskan parameter mana yang maknanya terikat posisi dan parameter mana yang paling terbaca bila disebut namanya.

Bagi fungsi yang dikembangkan bersama, manfaatnya terasa pada pemeliharaan. Nama parameter positional-only boleh diganti tanpa mematahkan pemanggil mana pun, karena tidak ada pemanggil yang menyebutnya. Sebaliknya, parameter penting yang rawan salah urut, seperti pilihan verifikasi=True, lebih aman dinyatakan keyword-only sehingga maksudnya terbaca pada baris pemanggilnya.

Contoh dari fungsi bawaan dapat dijalankan langsung. Fungsi len() menolak penyebutan namanya karena parameternya positional-only.

python Contoh: fungsi bawaan yang menolak argumen kata kunci
try:
    print(len(obj="abc"))
except TypeError as e:
    print(type(e).__name__, str(e))
text Keluaran
TypeError len() takes no keyword arguments

Pola yang sama diterapkan banyak fungsi pustaka standar modern, sehingga pembaca akan menemuinya di banyak tempat. Menuliskan penanda ini pada fungsi sendiri menyatakan niat yang sama, yaitu antarmuka dirancang dengan sengaja, bukan urutan parameter yang muncul secara kebetulan.

Kesalahan umum#

Kesalahan pertama adalah mengira parameter keyword-only selalu opsional. Sifat keyword-only hanya mengatur cara pengisiannya, sedangkan kewajibannya tetap ditentukan oleh kehadiran nilai default.

python Contoh: keyword-only tanpa default wajib diisi
def ukur(*, tinggi, berat):
    return tinggi, berat

print(ukur(tinggi=170, berat=60))
try:
    ukur(170, 60)
except TypeError as e:
    print(type(e).__name__, str(e))
text Keluaran
(170, 60)
TypeError ukur() takes 0 positional arguments but 2 were given

Tanda * sendirian tidak mengumpulkan argumen apa pun, sehingga pemanggilan dengan dua argumen posisi ditolak. Perhatikan pula bahwa kewajiban mengisi tinggi dan berat tetap berlaku meskipun keduanya tidak memiliki default.

Kesalahan kedua adalah menuliskan urutan penanda yang terbalik. Tanda / wajib mendahului tanda *, dan pelanggarannya ditolak sebagai SyntaxError sebelum program berjalan.

python
# tidak-dijalankan
def salah(a, *, b, /):   # SyntaxError: / harus mendahului *
    return a + b

Kesalahan ketiga adalah mengira parameter keyword-only dengan dan tanpa default tidak boleh bercampur. Pada kelompok keyword-only, parameter tanpa default boleh ditulis setelah parameter yang memiliki default, bentuk yang justru ditolak untuk parameter biasa. Fleksibilitas ini muncul karena posisi pada kelompok tersebut tidak lagi bermakna.

Ringkasan#

  • Parameter setelah * bersifat keyword-only dan wajib disebut dengan nama, sedangkan parameter sebelum / bersifat positional-only dan wajib diisi secara posisi.
  • Urutan lengkapnya tetap, yaitu parameter positional-only, /, parameter biasa, *, parameter keyword-only, dan **kwargs.
  • Keyword-only hanya mengatur cara pengisian, sedangkan kewajiban mengisi tetap ditentukan oleh nilai default.
  • Kedua penanda menjadikan aturan pemanggilan sebagai kontrak, sehingga nama dan urutan parameter pada sisi yang terkunci bebas disunting.

Referensi#