Câu hỏi * args và ** kwargs? [bản sao]


Câu hỏi này đã có câu trả lời ở đây:

Vì vậy, tôi gặp khó khăn với khái niệm về *args và **kwargs.

Cho đến nay tôi đã học được rằng:

  • *args = danh sách các đối số - như các đối số vị trí
  • **kwargs = dictionary - các khóa của nó trở thành các đối số từ khóa riêng biệt và các giá trị trở thành các giá trị của các đối số này.

Tôi không hiểu nhiệm vụ lập trình nào sẽ hữu ích.

Có lẽ:

Tôi nghĩ rằng để nhập danh sách và từ điển làm đối số của một hàm AND cùng lúc với ký tự đại diện, vì vậy tôi có thể chuyển bất kỳ đối số nào?

Có một ví dụ đơn giản nào để giải thích *args và **kwargs được sử dụng?

Ngoài ra các hướng dẫn tôi tìm thấy được sử dụng chỉ là "*" và một tên biến.

*args và **kwargs chỉ là trình giữ chỗ hoặc bạn có sử dụng chính xác không *args và **kwargs trong mã?


1195
2017-08-03 08:28


gốc


Một khi bạn nắm bắt được những điều này, bạn sẽ không bao giờ muốn bỏ lỡ chúng (đặc biệt, nếu bạn đã từng phải đối phó với PHP func_*_args()). - Boldewyn
Bạn đang đọc hướng dẫn nào? Vui lòng cập nhật câu hỏi của bạn với tiêu đề hoặc liên kết đến hướng dẫn mà bạn đã thấy điều này. - S.Lott
Các tài liệu đang ở docs.python.org/faq/programming.html#id13, btw. - mlvljr
Trên thực tế, 2 định dạng đối số này có thể được thêm vào bất kỳ khai báo hàm nào miễn là chúng là cuối cùng 2. Lưu ý thứ tự: explicit args, then *args, then **kwargs. ví dụ. def foo (arg1, arg2, *args, **kwargs): ... - smwikipedia
Giải thích chính xác. Kiểu dữ liệu của từ khóa này là chìa khóa quan trọng nhất để hiểu hoạt động của nó. - Johnny Wong


Các câu trả lời:


Cú pháp là * và **. Tên *args và **kwargs chỉ theo quy ước nhưng không có yêu cầu khó để sử dụng chúng.

Bạn sẽ sử dụng *args khi bạn không chắc chắn có bao nhiêu đối số có thể được chuyển đến hàm của bạn, nghĩa là nó cho phép bạn chuyển số lượng đối số tùy ý đến hàm của bạn. Ví dụ:

>>> def print_everything(*args):
        for count, thing in enumerate(args):
...         print( '{0}. {1}'.format(count, thing))
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage

Tương tự, **kwargs cho phép bạn xử lý các đối số được đặt tên mà bạn chưa xác định trước:

>>> def table_things(**kwargs):
...     for name, value in kwargs.items():
...         print( '{0} = {1}'.format(name, value))
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit

Bạn có thể sử dụng chúng cùng với các đối số được đặt tên. Các đối số rõ ràng nhận được các giá trị đầu tiên và sau đó mọi thứ khác được chuyển đến *args và **kwargs. Các đối số được đặt tên đến đầu tiên trong danh sách. Ví dụ:

def table_things(titlestring, **kwargs)

Bạn cũng có thể sử dụng cả hai trong cùng một định nghĩa hàm nhưng *args phải xảy ra trước **kwargs.

Bạn cũng có thể sử dụng * và ** cú pháp khi gọi một hàm. Ví dụ:

>>> def print_three_things(a, b, c):
...     print( 'a = {0}, b = {1}, c = {2}'.format(a,b,c))
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark, b = baboon, c = cat

Như bạn có thể thấy trong trường hợp này phải mất danh sách (hoặc tuple) của các mục và unpacks nó. Bằng cách này, nó khớp với chúng với các đối số trong hàm. Tất nhiên, bạn có thể có một * cả trong định nghĩa hàm và trong hàm gọi.


1476
2017-08-03 08:38



làm thế nào bạn sẽ nhìn này trong python giúp đỡ / tài liệu? - Alex
@Alex: Đây - Mr_and_Mrs_D
Có vẻ như không thể mở rộng danh sách được chuyển thành đối số vị trí ở giữa cuộc gọi hàm như trong function_call(arg1,arg2,*expanded_list_args,arg4,arg5). Danh sách mở rộng chỉ có thể được theo sau bởi các đối số từ khóa mà tôi tin. Có cách nào để vượt qua điều đó không? - mlh3789
@ mlh3789 có, và điều này làm việc với python3, chỉ. Nhưng những gì thực sự là một chút lạ: kinda này hoạt động trên các bài tập: a, b, *c, d, e = 1, 2, 3, 4, 5, 6 gán c cho [3, 4]. Một chút bối rối - Christian Tismer
Trong ví dụ trên, mylist giống như an array trong JS. Phương pháp bình thường của chúng ta print_three_things mất 3 args. Chuyển nó tới print_three_things (*mylist), các * chú thích nhiều hay ít giống như spreading operator trong ES6. Hãy cho tôi biết nếu xem xét của tôi là okay hay sai? Cảm ơn - Pristine Kallio


Một nơi mà việc sử dụng *args và **kwargs là khá hữu ích là cho subclassing.

class Foo(object):
    def __init__(self, value1, value2):
        # do something with the values
        print value1, value2

class MyFoo(Foo):
    def __init__(self, *args, **kwargs):
        # do something else, don't care about the args
        print 'myfoo'
        super(MyFoo, self).__init__(*args, **kwargs)

Bằng cách này bạn có thể mở rộng hành vi của lớp Foo, mà không cần phải biết quá nhiều về Foo. Điều này có thể khá thuận tiện nếu bạn đang lập trình cho một API có thể thay đổi. MyFoo chỉ chuyển tất cả các đối số cho lớp Foo.


443
2017-08-03 08:39



Câu trả lời này thực sự chỉ có ý nghĩa nếu bạn đã hiểu * và **. - Scott David Tesler
Tôi không hiểu. Nếu API của bạn thay đổi, bạn vẫn phải thay đổi tất cả những nơi bạn khởi tạo các lớp con. Và nếu bạn đang thay đổi tất cả những nơi đó, thì bạn cũng có thể có chữ ký của lớp trẻ em được cố định, thay vì bị tấn công cùng với args và kwargs không có lý do. Lý do của bạn về điều này không đòi hỏi kiến ​​thức về Foo là vô nghĩa, bởi vì ngay sau khi chữ ký thiết lập của các thay đổi constructor Foo, tất cả các cuộc gọi instantiation MyFoo của bạn cũng sẽ phải thay đổi. Điều này đòi hỏi kiến ​​thức về Foo và các tham số mà hàm tạo của nó yêu cầu. - Zoran Pavlovic
@ZoranPavlovic Một ví dụ mà điều này có thể được sử dụng là trong một tình huống mà bạn đang cung cấp một add-on cho một sản phẩm hiện có và muốn ghi đè / mở rộng một số chức năng. Bằng cách sử dụng * args và ** kwargs, phần bổ trợ này sẽ tiếp tục hoạt động nếu sản phẩm cơ bản thay đổi. Tuy nhiên, instantiating MyFoo xảy ra bên ngoài add-on. Điều đó có làm cho nó ý nghĩa hơn không? (Có nói điều này: một trang trí là một ví dụ tốt hơn khi bạn có thể sử dụng * args và ** kwargs.) - Mark van Lent
làm thế nào để bạn mở rộng này cho các đối số cụ thể của lớp con? - Hanan Shteingart
@HananShteingart Ví dụ như thế này: def __init __ (self, foo, * args, ** kwargs) - Mark van Lent


Dưới đây là ví dụ sử dụng 3 loại thông số khác nhau.

def func(required_arg, *args, **kwargs):
    # required_arg is a positional-only parameter.
    print required_arg

    # args is a tuple of positional arguments,
    # because the parameter name has * prepended.
    if args: # If args is not empty.
        print args

    # kwargs is a dictionary of keyword arguments,
    # because the parameter name has ** prepended.
    if kwargs: # If kwargs is not empty.
        print kwargs

>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes at least 1 argument (0 given)

>>> func("required argument")
required argument

>>> func("required argument", 1, 2, '3')
required argument
(1, 2, '3')

>>> func("required argument", 1, 2, '3', keyword1=4, keyword2="foo")
required argument
(1, 2, '3')
{'keyword2': 'foo', 'keyword1': 4}

279
2017-08-03 08:42





Đây là một trong những nơi yêu thích của tôi để sử dụng ** cú pháp như trong ví dụ cuối cùng của Dave Webb:

mynum = 1000
mystr = 'Hello World!'
print "{mystr} New-style formatting is {mynum}x more fun!".format(**locals())

Tôi không chắc liệu nó có nhanh đến mức nào khi so sánh với việc chỉ sử dụng tên của chính họ, nhưng rất dễ dàng để gõ!


65
2017-08-03 13:03



này, anh chàng này đã phát minh ra Python 3,6 chuỗi f trước khi nó được mát mẻ - cat
@cat xin giải thích? bạn đang đề cập đến chính xác điều gì? không phải format phương pháp đã tồn tại ngay cả trong Python 2? - dabadaba
@dabadaba f-strings, không phải chuỗi định dạng (ví dụ: f'{mystr} New-style formatting is {mynum}x more fun!') - Wayne Werner
@WayneWerner oh Tôi hiểu rồi, tôi không biết về cái mới này! cảm ơn - dabadaba
@dabadaba chúc mừng trở thành một trong những ngày hôm nay may mắn 10k - Wayne Werner


Một trường hợp mà * args và ** kwargs là hữu ích khi viết các hàm bao bọc (chẳng hạn như các trình trang trí) cần phải có khả năng chấp nhận các đối số tùy ý để truyền cho hàm được bao bọc. Ví dụ: một trình trang trí đơn giản in các đối số và giá trị trả về của hàm đang được bao bọc:

def mydecorator( f ):
   @functools.wraps( f )
   def wrapper( *args, **kwargs ):
      print "Calling f", args, kwargs
      v = f( *args, **kwargs )
      print "f returned", v
      return v
   return wrapper

39
2017-08-03 08:40





* args và ** kwargs là các tính năng đặc biệt của Python. Hãy suy nghĩ về một hàm có thể có số lượng đối số không xác định. Ví dụ, vì bất kỳ lý do gì, bạn muốn có hàm tổng hợp một số chưa biết của các số (và bạn không muốn sử dụng hàm tổng hợp dựng sẵn). Vì vậy, bạn viết hàm này:

def sumFunction(*args):
  result = 0
  for x in args:
    result += x
  return result

và sử dụng nó như: sumFunction (3,4,6,3,6,8,9).

** kwargs có chức năng khác. Với ** kwargs bạn có thể đưa ra các đối số từ khóa tùy ý cho một hàm và bạn có thể truy cập chúng dưới dạng dictonary.

def someFunction(**kwargs):
  if 'text' in kwargs:
    print kwargs['text']

Gọi someFunction (text = "foo") sẽ in foo.


35
2017-08-03 08:40





Chỉ cần tưởng tượng bạn có một chức năng nhưng bạn không muốn hạn chế số tham số cần. Thí dụ:

>>> import operator
>>> def multiply(*args):
...  return reduce(operator.mul, args)

Sau đó, bạn sử dụng chức năng này như:

>>> multiply(1,2,3)
6

or

>>> numbers = [1,2,3]
>>> multiply(*numbers)
6

17
2017-08-03 08:40





Tên *args và **kwargs hoặc là **kw hoàn toàn là theo quy ước. Nó giúp chúng ta dễ dàng đọc mã của nhau

Một nơi thuận tiện là khi sử dụng mô-đun struct

struct.unpack() trả về một tuple trong khi struct.pack() sử dụng một số biến đối số. Khi thao tác dữ liệu thuận tiện để có thể chuyển một bộ tuple tới struck.pack() ví dụ.

tuple_of_data = struct.unpack(format_str, data)
... manipulate the data
new_data = struct.pack(format_str, *tuple_of_data)

không có khả năng này, bạn sẽ bị buộc phải viết

new_data = struct.pack(format_str, tuple_of_data[0], tuple_of_data[1], tuple_of_data[2],...)

điều này cũng có nghĩa là nếu thay đổi format_str và kích thước của các thay đổi tuple, tôi sẽ phải quay lại và chỉnh sửa dòng dài đó


14
2017-08-03 08:32