Apa yang dilakukan **
(bintang ganda) dan *
(bintang) lakukan untuk parameter
Mereka memungkinkan fungsi didefinisikan untuk menerima dan bagi pengguna untuk memberikan sejumlah argumen, posisi ( *
) dan kata kunci ( **
).
Mendefinisikan Fungsi
*args
memungkinkan sejumlah argumen posisi opsional (parameter), yang akan ditugaskan ke sebuah tuple bernama args
.
**kwargs
memungkinkan sejumlah argumen kata kunci opsional (parameter), yang akan berada dalam nama yang ditentukan kwargs
.
Anda dapat (dan harus) memilih nama yang sesuai, tetapi jika maksudnya argumennya adalah semantik non-spesifik, args
dan kwargs
merupakan nama standar.
Ekspansi, Melewati sejumlah argumen
Anda juga dapat menggunakan *args
dan **kwargs
mengirimkan parameter dari daftar (atau setiap iterable) dan dicts (atau pemetaan apa pun), masing-masing.
Fungsi menerima parameter tidak harus tahu bahwa mereka sedang diperluas.
Misalnya, xrange Python 2 tidak secara eksplisit berharap *args
, tetapi karena dibutuhkan 3 bilangan bulat sebagai argumen:
>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x) # expand here
xrange(0, 2, 2)
Sebagai contoh lain, kita dapat menggunakan ekspansi dict di str.format
:
>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'
Baru di Python 3: Menentukan fungsi dengan argumen hanya kata kunci
Anda dapat memiliki argumen hanya kata kunci setelah *args
- misalnya, di sini, kwarg2
harus diberikan sebagai argumen kata kunci - tidak secara posisi:
def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs):
return arg, kwarg, args, kwarg2, kwargs
Pemakaian:
>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})
Juga, *
dapat digunakan dengan sendirinya untuk menunjukkan bahwa hanya mengikuti kata kunci argumen, tanpa memungkinkan untuk argumen posisi tak terbatas.
def foo(arg, kwarg=None, *, kwarg2=None, **kwargs):
return arg, kwarg, kwarg2, kwargs
Di sini, kwarg2
sekali lagi harus berupa argumen kata kunci yang dinamai secara eksplisit:
>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})
Dan kami tidak dapat lagi menerima argumen posisi yang tidak terbatas karena kami tidak memiliki *args*
:
>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments
but 5 positional arguments (and 1 keyword-only argument) were given
Sekali lagi, lebih sederhana, di sini kita perlu kwarg
diberi nama, bukan secara posisi:
def bar(*, kwarg=None):
return kwarg
Dalam contoh ini, kita melihat bahwa jika kita mencoba untuk melewati kwarg
posisi, kita mendapatkan kesalahan:
>>> bar('kwarg')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given
Kami harus secara eksplisit melewatkan kwarg
parameter sebagai argumen kata kunci.
>>> bar(kwarg='kwarg')
'kwarg'
Demo yang kompatibel dengan Python 2
*args
(biasanya dikatakan "star-args") dan **kwargs
(bintang dapat tersirat dengan mengatakan "kwargs", tetapi secara eksplisit dengan "kwarg bintang ganda") adalah idiom umum dari Python untuk menggunakan *
dan **
notasi. Nama-nama variabel spesifik ini tidak diperlukan (misalnya Anda bisa menggunakan *foos
dan **bars
), tetapi keberangkatan dari konvensi cenderung membuat marah sesama pembuat kode Python Anda.
Kita biasanya menggunakan ini ketika kita tidak tahu apa fungsi kita akan menerima atau berapa banyak argumen yang mungkin kita lewati, dan kadang-kadang bahkan ketika penamaan setiap variabel secara terpisah akan menjadi sangat berantakan dan berlebihan (tetapi ini adalah kasus di mana biasanya eksplisit adalah lebih baik daripada implisit).
Contoh 1
Fungsi berikut menjelaskan bagaimana mereka dapat digunakan, dan menunjukkan perilaku. Perhatikan b
argumen bernama akan dikonsumsi oleh argumen posisi kedua sebelum:
def foo(a, b=10, *args, **kwargs):
'''
this function takes required argument a, not required keyword argument b
and any number of unknown positional arguments and keyword arguments after
'''
print('a is a required argument, and its value is {0}'.format(a))
print('b not required, its default value is 10, actual value: {0}'.format(b))
# we can inspect the unknown arguments we were passed:
# - args:
print('args is of type {0} and length {1}'.format(type(args), len(args)))
for arg in args:
print('unknown arg: {0}'.format(arg))
# - kwargs:
print('kwargs is of type {0} and length {1}'.format(type(kwargs),
len(kwargs)))
for kw, arg in kwargs.items():
print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
# But we don't have to know anything about them
# to pass them to other functions.
print('Args or kwargs can be passed without knowing what they are.')
# max can take two or more positional args: max(a, b, c...)
print('e.g. max(a, b, *args) \n{0}'.format(
max(a, b, *args)))
kweg = 'dict({0})'.format( # named args same as unknown kwargs
', '.join('{k}={v}'.format(k=k, v=v)
for k, v in sorted(kwargs.items())))
print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
dict(**kwargs), kweg=kweg))
Kami dapat memeriksa bantuan online untuk tanda tangan fungsi, dengan help(foo)
, yang memberi tahu kami
foo(a, b=10, *args, **kwargs)
Mari kita panggil fungsi ini dengan foo(1, 2, 3, 4, e=5, f=6, g=7)
yang mencetak:
a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns:
{'e': 5, 'g': 7, 'f': 6}
Contoh 2
Kami juga dapat menyebutnya menggunakan fungsi lain, yang hanya kami sediakan a
:
def bar(a):
b, c, d, e, f = 2, 3, 4, 5, 6
# dumping every local variable into foo as a keyword argument
# by expanding the locals dict:
foo(**locals())
bar(100)
cetakan:
a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns:
{'c': 3, 'e': 5, 'd': 4, 'f': 6}
Contoh 3: penggunaan praktis dalam dekorator
OK, jadi mungkin kita belum melihat utilitasnya. Jadi bayangkan Anda memiliki beberapa fungsi dengan kode redundan sebelum dan / atau setelah kode pembeda. Fungsi bernama berikut ini hanya pseudo-code untuk tujuan ilustrasi.
def foo(a, b, c, d=0, e=100):
# imagine this is much more code than a simple function call
preprocess()
differentiating_process_foo(a,b,c,d,e)
# imagine this is much more code than a simple function call
postprocess()
def bar(a, b, c=None, d=0, e=100, f=None):
preprocess()
differentiating_process_bar(a,b,c,d,e,f)
postprocess()
def baz(a, b, c, d, e, f):
... and so on
Kami mungkin dapat menangani hal ini secara berbeda, tetapi kami dapat mengekstraksi redundansi dengan dekorator, dan contoh kami di bawah ini menunjukkan bagaimana *args
dan **kwargs
bisa sangat berguna:
def decorator(function):
'''function to wrap other functions with a pre- and postprocess'''
@functools.wraps(function) # applies module, name, and docstring to wrapper
def wrapper(*args, **kwargs):
# again, imagine this is complicated, but we only write it once!
preprocess()
function(*args, **kwargs)
postprocess()
return wrapper
Dan sekarang setiap fungsi yang dibungkus dapat ditulis lebih ringkas, karena kami telah memperhitungkan redundansi:
@decorator
def foo(a, b, c, d=0, e=100):
differentiating_process_foo(a,b,c,d,e)
@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
differentiating_process_bar(a,b,c,d,e,f)
@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
differentiating_process_baz(a,b,c,d,e,f, g)
@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
differentiating_process_quux(a,b,c,d,e,f,g,h)
Dan oleh anjak keluar kode kita, yang *args
dan **kwargs
memungkinkan kita untuk melakukan, kita mengurangi baris kode, lebih mudah dibaca dan rawatan, dan memiliki lokasi kanonik tunggal untuk logika dalam program kami. Jika kita perlu mengubah bagian mana pun dari struktur ini, kita memiliki satu tempat untuk membuat setiap perubahan.