Saya selalu terkejut bahwa untuk bekerja dengan argumen fungsi Python, Anda hanya perlu memahami
*argsdan **kwargs. Dan saya terkejut tidak sia-sia. Ternyata, perdebatan itu jauh dari kata mudah. Dalam posting ini, saya ingin memberikan gambaran umum tentang segala sesuatu yang berhubungan dengan argumen fungsi dengan Python. Saya berharap pada akhirnya saya benar-benar dapat menunjukkan gambaran umum bekerja dengan argumen, dan artikel ini tidak akan menjadi publikasi lain di mana pembaca tidak akan dapat menemukan sesuatu yang baru. Dan sekarang - langsung ke intinya.
Sebagian besar pembaca artikel ini, saya yakin, memahami esensi argumen fungsi. Untuk pemula, izinkan saya menjelaskan bahwa ini adalah objek yang dikirim ke suatu fungsi oleh inisiator pemanggilannya. Saat meneruskan argumen ke suatu fungsi, banyak tindakan yang dilakukan, bergantung pada jenis objek yang dikirim ke fungsi (objek yang bisa berubah atau tidak berubah). Inisiator panggilan fungsi adalah entitas yang memanggil fungsi dan memberikan argumen padanya. Berbicara mengenai fungsi pemanggil, ada beberapa hal yang perlu diperhatikan yang sekarang akan kita bahas.
Argumen, yang namanya ditentukan saat fungsi dideklarasikan, menyimpan objek yang diteruskan ke fungsi saat dipanggil. Selain itu, jika ada sesuatu yang ditugaskan ke variabel lokal terkait dari fungsi, parameternya, operasi ini tidak mempengaruhi objek tetap yang diteruskan ke fungsi. Misalnya:
def foo(a):
a = a+5
print(a) # 15
a = 10
foo(a)
print(a) # 10
Seperti yang Anda lihat, pemanggilan fungsi tidak memengaruhi variabel dengan cara apa pun
a. Inilah tepatnya yang terjadi ketika objek yang tidak dapat diubah diteruskan ke suatu fungsi.
Dan jika objek yang bisa berubah diteruskan ke fungsi, Anda mungkin mengalami perilaku sistem yang berbeda dari yang di atas.
def foo(lst):
lst = lst + ['new entry']
print(lst) # ['Book', 'Pen', 'new entry']
lst = ['Book', 'Pen']
print(lst) # ['Book', 'Pen']
foo(lst)
print(lst) # ['Book', 'Pen']
Pernahkah Anda memperhatikan sesuatu yang baru di sini? Jika Anda menjawab "Tidak," Anda benar. Tetapi jika kita entah bagaimana memengaruhi elemen objek yang bisa berubah yang diteruskan ke fungsi tersebut, kita akan menyaksikan sesuatu yang berbeda.
def foo(lst):
lst[1] = 'new entry'
print(lst) # ['Book', 'new entry']
lst = ['Book', 'Pen']
print(lst) # ['Book', 'Pen']
foo(lst)
print(lst) # ['Book', 'new entry']
Seperti yang Anda lihat, objek dari parameter
lstdiubah setelah pemanggilan fungsi. Ini terjadi karena fakta bahwa kami bekerja dengan referensi ke objek yang disimpan dalam parameter lst. Akibatnya, mengubah konten objek ini berada di luar cakupan fungsi. Anda dapat menghindari ini hanya dengan membuat salinan mendalam dari objek tersebut dan menuliskannya ke variabel lokal fungsi.
def foo(lst):
lst = lst[:]
lst[1] = 'new entry'
print(lst) # ['Book', 'new entry']
lst = ['Book', 'Pen']
print(lst) # ['Book', 'Pen']
foo(lst)
print(lst) # ['Book', 'Pen']
Bukankah itu sudah mengejutkanmu? Jika tidak, saya ingin memastikan bahwa Anda melewatkan apa yang Anda ketahui dan segera beralih ke materi baru untuk Anda. Dan jika ya - maka, tandai kata-kata saya, Anda, dengan mengenal argumennya, akan belajar lebih banyak hal menarik.
Jadi, inilah yang harus Anda ketahui tentang argumen fungsi:
- Urutan argumen posisi yang diteruskan ke fungsi.
- Urutan di mana argumen bernama diteruskan ke fungsi.
- Menetapkan nilai argumen default.
- Organisasi pemrosesan set argumen dengan panjang variabel.
- Membongkar argumen.
- Menggunakan argumen yang hanya dapat diberikan dengan nama (hanya kata kunci).
Mari kita lihat masing-masing poin ini.
1. Urutan meneruskan argumen posisi ke fungsi
Argumen posisi diproses dari kiri ke kanan. Artinya, ternyata posisi argumen yang diteruskan ke fungsi tersebut bersesuaian langsung dengan posisi parameter yang digunakan di header fungsi ketika dideklarasikan.
def foo(d, e, f):
print(d, e, f)
a, b, c = 1, 2, 3
foo(a, b, c) # 1, 2, 3
foo(b, a, c) # 2, 1, 3
foo(c, b, a) # 3, 2, 1
Variabel
a, bdan cmemiliki nilai 1, 2 dan 3. Variabel ini memainkan peran argumen yang dengannya fungsi tersebut dipanggil foo. Mereka, pada panggilan pertama fungsi, sesuai dengan parameter d, edan f. Mekanisme ini berlaku untuk hampir semua 6 poin di atas tentang apa yang perlu Anda ketahui tentang argumen fungsi dengan Python. Lokasi dari argumen posisi yang diteruskan ke fungsi saat dipanggil memainkan peran utama dalam menetapkan nilai ke parameter fungsi.
2. Urutan untuk meneruskan argumen bernama ke fungsi
Argumen bernama diteruskan ke fungsi dengan nama argumen ini sesuai dengan nama yang diberikan padanya saat fungsi itu dideklarasikan.
def foo(arg1=0, arg2=0, arg3=0):
print(arg1, arg2, arg3)
a, b, c = 1, 2, 3
foo(a,b,c) # 1 2 3
foo(arg1=a, arg2=b, arg3=c) # 1 2 3
foo(arg3=c, arg2=b, arg1=a) # 1 2 3
foo(arg2=b, arg1=a, arg3=c) # 1 2 3
Seperti yang Anda lihat, fungsi tersebut
foomembutuhkan 3 argumen. Argumen ini diberi nama arg1, arg2dan arg3. Perhatikan bagaimana kita mengubah posisi argumen saat memanggil fungsi. Argumen yang diberi nama diperlakukan berbeda dari argumen posisi, meskipun sistem terus membacanya dari kiri ke kanan. Python mempertimbangkan nama argumen, bukan posisinya, saat memberikan nilai yang sesuai ke parameter fungsi. Hasilnya, ternyata fungsi tersebut menghasilkan hal yang sama terlepas dari posisi argumen yang diteruskan kepadanya. Selalu begitu 1 2 3.
Harap dicatat bahwa mekanisme yang dijelaskan dalam paragraf 1 terus beroperasi di sini.
3. Menetapkan nilai argumen default
Nilai default dapat diberikan ke argumen bernama. Saat menggunakan mekanisme ini dalam suatu fungsi, argumen tertentu menjadi opsional. Deklarasi fungsi tersebut terlihat seperti yang kita pertimbangkan di poin # 2. Satu-satunya perbedaan adalah bagaimana fungsi-fungsi ini dipanggil.
def foo(arg1=0, arg2=0, arg3=0):
print(arg1, arg2, arg3)
a, b, c = 1, 2, 3
foo(arg1=a) # 1 0 0
foo(arg1=a, arg2=b ) # 1 2 0
foo(arg1=a, arg2=b, arg3=c) # 1 2 3
Harap dicatat bahwa dalam contoh ini kami tidak meneruskan semua argumen ke fungsi seperti yang dijelaskan dalam deklarasinya. Dalam kasus ini, parameter terkait diberi nilai default. Mari lanjutkan dengan contoh ini:
foo(arg2=b) # 0 2 0
foo(arg2=b, arg3=c ) # 0 2 3
foo(arg3=c) # 0 0 3
foo(arg3=c, arg1=a ) # 1 0 3
Ini adalah contoh sederhana dan mudah dipahami dari penggunaan mekanisme yang dijelaskan di atas untuk memanggil fungsi dengan meneruskan argumen bernama ke sana. Sekarang mari kita memperumit eksperimen kita dengan menggabungkan apa yang telah kita bicarakan sejauh ini dalam poin # 1, # 2 dan # 3:
foo(a, arg2=b) # 1 2 0
foo(a, arg2=b, arg3=c) # 1 2 3
foo(a, b, arg3=c) # 1 2 3
foo(a) # 1 0 0
foo(a,b) # 1 2 0
Di sini, argumen posisional dan bernama digunakan saat memanggil fungsi. Saat menggunakan argumen posisi, urutan penetapannya terus memainkan peran penting dalam meneruskan input ke fungsi dengan benar.
Di sini saya ingin menarik perhatian Anda pada satu detail yang luar biasa. Terdiri dari argumen posisi yang tidak dapat ditentukan setelah argumen bernama. Berikut adalah contoh untuk membantu Anda memahami gagasan ini dengan lebih baik:
foo(arg1=a, b)
>>>
foo(arg1=a, b)
^
SyntaxError: positional argument follows keyword argument
foo(a, arg2=b, c)
>>>
foo(a, arg2=b, c)
^
SyntaxError: positional argument follows keyword argument
Anda bisa menganggapnya sebagai aturan. Argumen posisi tidak harus mengikuti argumen bernama saat memanggil fungsi.
4. Organisasi pemrosesan set argumen dengan panjang variabel
Di sini kita akan berbicara tentang konstruksi
*argsdan **kwargs. Saat konstruksi ini digunakan dalam deklarasi fungsi, kami berharap bahwa saat fungsi dipanggil, kumpulan argumen dengan panjang sembarang akan direpresentasikan sebagai parameter argsdan kwargs. Ketika konstruksi diterapkan *args, parameter argsmenerima argumen posisi yang direpresentasikan sebagai tupel. Saat diterapkan **kwargsdalam kwargsargumen bernama musim gugur, tercantum dalam kamus.
def foo(*args):
print(args)
a, b, c = 1, 2, 3
foo(a, b, c) # (1, 2, 3)
foo(a, b) # (1, 2)
foo(a) # (1)
foo(b, c) # (2, 3)
Kode ini membuktikan bahwa parameter
argsmenyimpan tupel yang berisi apa yang diteruskan ke fungsi saat dipanggil.
def foo(**kwargs):
print(kwargs)
foo(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
foo(a=1, b=2) # {'a': 1, 'b': 2}
foo(a=1) # {'a': 1}
foo(b=2, c=3) # {'b': 2, 'c': 3}
Kode di atas menunjukkan bahwa parameter
kwargsmenyimpan kamus pasangan nilai kunci yang mewakili argumen bernama yang diteruskan ke fungsi saat dipanggil.
Namun, perlu dicatat bahwa fungsi yang dirancang untuk menerima argumen posisi tidak dapat diberikan argumen bernama (dan sebaliknya).
def foo(*args):
print(args)
foo(a=1, b=2, c=3)
>>>
foo(a=1, b=2, c=3)
TypeError: foo() got an unexpected keyword argument 'a'
#########################################################
def foo(**kwargs):
print(kwargs)
a, b, c = 1, 2, 3
foo(a, b, c)
>>>
TypeError: foo() takes 0 positional arguments but 3 were given
Sekarang mari kita kumpulkan semua yang kita analisis di poin # 1, # 2, # 3 dan # 4, dan bereksperimen dengan semua ini, memeriksa kombinasi argumen yang berbeda yang dapat diteruskan ke fungsi saat dipanggil.
def foo(*args,**kwargs):
print(args, kwargs)
foo(a=1,)
# () {'a': 1}
foo(a=1, b=2, c=3)
# () {'a': 1, 'b': 2, 'c': 3}
foo(1, 2, a=1, b=2)
# (1, 2) {'a': 1, 'b': 2}
foo(1, 2)
# (1, 2) {}
Seperti yang Anda lihat, kami memiliki tupel
argsdan kamus yang dapat kami gunakan kwargs.
Dan ini aturan lainnya. Itu terletak pada fakta bahwa struktur
*argstidak dapat digunakan setelah struktur **kwargs.
def foo(**kwargs, *args):
print(kwargs, args)
>>>
def foo(**kwargs, *args):
^
SyntaxError: invalid syntax
Aturan yang sama berlaku untuk urutan argumen yang ditentukan saat memanggil fungsi. Argumen posisi tidak boleh mengikuti argumen bernama.
foo(a=1, 1)
>>>
foo(a=1, 1)
^
SyntaxError: positional argument follows keyword argument
foo(1, a=1, 2)
>>>
foo(1, a=1, 2)
^
SyntaxError: positional argument follows keyword argument
Saat mendeklarasikan fungsi, Anda bisa menggabungkan argumen posisi,
*argsdan *kwagrssebagai berikut:
def foo(var, *args,**kwargs):
print(var, args, kwargs)
foo(1, a=1,) # 1
# 1 () {'a': 1}
foo(1, a=1, b=2, c=3) # 2
# 1 () {'a': 1, 'b': 2, 'c': 3}
foo(1, 2, a=1, b=2) # 3
# 1 (2,) {'a': 1, 'b': 2}
foo(1, 2, 3, a=1, b=2) # 4
# 1 (2, 3) {'a': 1, 'b': 2}
foo(1, 2) # 5
# 1 (2,) {}
Saat mendeklarasikan suatu fungsi,
fookami berasumsi bahwa fungsi tersebut harus memiliki satu argumen posisi yang diperlukan. Ini diikuti oleh satu set argumen posisi panjang variabel, dan set ini diikuti oleh satu set argumen bernama panjang variabel. Mengetahui hal ini, kita dapat dengan mudah "mendekripsi" setiap pemanggilan fungsi di atas.
The
1Fungsi dilewatkan argumen 1dan a=1. Ini adalah, masing-masing, argumen posisional dan bernama. 2Beragam 1. Di sini, panjang set argumen posisi adalah nol.
Di
3kami melewati fungsi 1, 2dan a=1,b=2. Ini berarti bahwa sekarang menerima dua argumen posisi dan dua argumen bernama. Menurut deklarasi fungsi, ternyata1diambil sebagai argumen posisi yang diperlukan, 2masuk ke dalam satu set argumen posisi panjang variabel, a=1dan b=2berakhir dalam satu set argumen bernama panjang variabel.
Untuk memanggil fungsi ini dengan benar, kita harus memberikan setidaknya satu argumen posisi ke sana. Jika tidak, kami akan menghadapi kesalahan.
def foo(var, *args,**kwargs):
print(var, args, kwargs)
foo(a=1)
>>>
foo(a=1)
TypeError: foo() missing 1 required positional argument: 'var'
Variasi lain dari fungsi ini adalah fungsi yang menyatakan bahwa dibutuhkan satu argumen posisi yang diperlukan dan satu argumen bernama, diikuti dengan set panjang variabel dari argumen posisi dan bernama.
def foo(var, kvar=0, *args,**kwargs):
print(var, kvar, args, kwargs)
foo(1, a=1,) # 1
# 1 0 () {'a': 1}
foo(1, 2, a=1, b=2, c=3) # 2
# 1 0 () {'a': 1, 'b': 2, 'c': 3}
foo(1, 2, 3, a=1, b=2) # 3
# 1 2 () {'a': 1, 'b': 2}
foo(1, 2, 3, 4, a=1, b=2) # 4
# 1 2 (3,) {'a': 1, 'b': 2}
foo(1, kvar=2) # 5
# 1 2 () {}
Panggilan ke fungsi ini dapat "didekripsi" dengan cara yang sama seperti yang dilakukan saat menganalisis fungsi sebelumnya.
Saat memanggil fungsi ini, harus diberikan setidaknya satu argumen posisi. Jika tidak, kami akan menemui kesalahan:
foo()
>>>
foo()
TypeError: foo() missing 1 required positional argument: 'var'
foo(1)
# 1 0 () {}
Perhatikan bahwa panggilan
foo(1)berfungsi dengan baik. Intinya di sini adalah bahwa jika suatu fungsi dipanggil tanpa menentukan nilai untuk argumen bernama, nilainya secara otomatis ditetapkan ke sana.
Dan berikut adalah beberapa kesalahan lagi yang dapat ditemui jika fungsi ini dipanggil dengan tidak benar:
foo(kvar=1) # 1
>>>
TypeError: foo() missing 1 required positional argument: 'var'
foo(kvar=1, 1, a=1) # 2
>>>
SyntaxError: positional argument follows keyword argument
foo(1, kvar=2, 3, a=2) # 3
>>>
SyntaxError: positional argument follows keyword argument
Perhatikan error runtime
3.
5. Membongkar argumen
Pada bagian sebelumnya, kita berbicara tentang bagaimana mengumpulkan set argumen yang diteruskan ke fungsi ke dalam tupel dan kamus. Dan di sini kita akan membahas operasi kebalikannya. Yaitu, kami akan menganalisis mekanisme yang memungkinkan Anda untuk mengekstrak argumen yang diberikan ke input fungsi.
args = (1, 2, 3, 4)
print(*args) # 1 2 3 4
print(args) # (1, 2, 3, 4)
kwargs = { 'a':1, 'b':2}
print(kwargs) # {'a': 1, 'b': 2}
print(*kwargs) # a b
Anda dapat mengekstrak variabel menggunakan sintaks
*dan **. Ini adalah bagaimana mereka digunakan saat melewatkan tupel, daftar, dan kamus ke suatu fungsi.
def foo(a, b=0, *args, **kwargs):
print(a, b, args, kwargs)
tup = (1, 2, 3, 4)
lst = [1, 2, 3, 4]
d = {'e':1, 'f':2, 'g':'3'}
foo(*tup) # foo(1, 2, 3, 4)
# 1 2 (3, 4) {}
foo(*lst) # foo(1, 2, 3, 4)
# 1 2 (3, 4) {}
foo(1, *tup) # foo(1, 1, 2, 3, 4)
# 1 1 (2, 3, 4) {}
foo(1, 5, *tup) # foo(1, 5, 1, 2, 3, 4)
# 1 5 (1, 2, 3, 4) {}
foo(1, *tup, **d) # foo(1, 1, 2, 3, 4 ,e=1 ,f=2, g=3)
# 1 1 (2, 3, 4) {'e': 1, 'f': 2, 'g': '3'}
foo(*tup, **d) # foo(1, 1, 2, 3, 4 ,e=1 ,f=2, g=3)
# 1 2 (3, 4) {'e': 1, 'f': 2, 'g': '3'}
d['b'] = 45
foo(2, **d) # foo(1, e=1 ,f=2, g=3, b=45)
# 2 45 () {'e': 1, 'f': 2, 'g': '3'}
Dekonstruksi setiap panggilan fungsi yang ditampilkan di sini menggunakan pembongkaran argumen dan perhatikan bagaimana panggilan terkait akan terlihat tanpa menggunakan
*dan **. Cobalah untuk memahami apa yang terjadi ketika Anda melakukan panggilan ini dan bagaimana berbagai struktur data dibongkar.
Bereksperimen dengan argumen pembongkaran, Anda mungkin menemukan kesalahan baru:
foo(1, *tup, b=5)
>>>
TypeError: foo() got multiple values for argument 'b'
foo(1, b=5, *tup)
>>>
TypeError: foo() got multiple values for argument 'b'
Kesalahan ini terjadi karena konflik antara argumen bernama ,,
b=5dan argumen posisi. Seperti yang kita temukan di bagian # 2, urutan argumen bernama tidak masalah saat diteruskan. Akibatnya, kesalahan yang sama terjadi pada kedua kasus tersebut.
6. Menggunakan argumen yang hanya bisa diberikan oleh nama (hanya kata kunci)
Dalam beberapa kasus, Anda perlu membuat fungsi menerima argumen bernama yang diperlukan. Jika, saat mendeklarasikan suatu fungsi, mereka mendeskripsikan argumen yang hanya dapat diteruskan dengan nama, maka argumen tersebut harus diteruskan ke sana setiap kali dipanggil.
def foo(a, *args, b):
print(a, args, b)
tup = (1, 2, 3, 4)
foo(*tup, b=35)
# 1 (2, 3, 4) 35
foo(1, *tup, b=35)
# 1 (1, 2, 3, 4) 35
foo(1, 5, *tup, b=35)
# 1 (5, 1, 2, 3, 4) 35
foo(1, *tup, b=35)
# 1 (1, 2, 3, 4) 35
foo(1, b=35)
# 1 () 35
foo(1, 2, b=35)
# 1 (2,) 35
foo(1)
# TypeError: foo() missing 1 required keyword-only argument: 'b'
foo(1, 2, 3)
# TypeError: foo() missing 1 required keyword-only argument: 'b'
Seperti yang Anda lihat, diharapkan bahwa fungsi tersebut akan diberikan argumen bernama
b, yang, dalam deklarasi fungsi, ditentukan setelahnya *args. Dalam hal ini, dalam deklarasi fungsi, Anda cukup menggunakan simbol *, setelah itu, dipisahkan dengan koma, ada pengenal argumen bernama yang dapat diteruskan ke fungsi hanya dengan nama. Fungsi seperti itu tidak akan dirancang untuk menerima sekumpulan argumen posisi panjang variabel.
def foo(a, *, b, c):
print(a, b, c)
tup = (1, 2, 3, 4)
foo(1, b=35, c=55)
# 1 35 55
foo(c= 55, b=35, a=1)
# 1 35 55
foo(1, 2, 3)
# TypeError: foo() takes 1 positional argument but 3 were given
foo(*tup, b=35)
# TypeError: foo() takes 1 positional argument but 4 positional arguments (and 1 keyword-only argument) were given
foo(1, b=35)
# TypeError: foo() takes 1 positional argument but 4 positional arguments (and 1 keyword-only argument) were given
Fungsi yang dideklarasikan dalam contoh sebelumnya mengambil satu argumen posisi dan dua argumen bernama, yang hanya bisa diteruskan dengan nama. Hal ini mengarah pada fakta bahwa agar fungsi dapat dipanggil dengan benar, ia harus meneruskan kedua argumen bernama. Setelah itu,
*Anda juga bisa mendeskripsikan argumen bernama, yang diberi nilai default. Ini memberi kita kebebasan dalam jumlah tertentu saat memanggil fungsi tersebut.
def foo(a, *, b=0, c, d=0):
print(a, b, c, d)
foo(1, c=55)
# 1 0 55 0
foo(1, c=55, b=35)
# 1 35 55 0
foo(1)
# TypeError: foo() missing 1 required keyword-only argument: 'c'
Perhatikan bahwa fungsi dapat dipanggil secara normal tanpa memberikan argumen apa pun padanya,
bdan dkarena telah diberi nilai default.
Hasil
Mungkin kita memiliki cerita yang sangat panjang tentang pertengkaran. Saya berharap para pembaca materi ini telah mempelajari sesuatu yang baru untuk diri mereka sendiri. Dan omong-omong, kisah argumen fungsi dengan Python terus berlanjut. Mungkin kita akan membicarakannya nanti.
Apakah Anda mempelajari sesuatu yang baru tentang argumen fungsi dengan Python dari materi ini?
