** (sao đôi / dấu sao) và * (sao / dấu sao) làm gì cho các tham số?


2350

Trong các định nghĩa phương pháp sau đây, cái gì ***làm gì param2?

def foo(param1, *param2):
def bar(param1, **param2):



Xem thêm stackoverflow.com/questions/14301967/ cho một dấu sao trần
naught101

24
Câu hỏi này là một mục tiêu trùng lặp rất phổ biến, nhưng thật không may, nó thường được sử dụng không chính xác. Hãy nhớ rằng câu hỏi này hỏi về việc xác định hàm với varargs ( def func(*args)). Đối với một câu hỏi hỏi ý nghĩa của nó trong hàm gọi ( func(*[1,2])) xem tại đây . Đối với một câu hỏi hỏi làm thế nào để giải nén danh sách đối số xem tại đây . Đối với một câu hỏi hỏi những gì *có nghĩa là trong chữ ( [*[1, 2]]) xem ở đây .
Aran-Fey

Bạn có thể tìm hiểu về cách sử dụng của nó trong định nghĩa hàm và hàm gọi tại đây: pythontips.com/2013/08/04/args-and-kwargs-in-python-explained
Akshay Anurag

Câu trả lời:


2237

Các *args**kwargslà một thành ngữ thông thường để cho phép số lượng tùy ý các đối số chức năng như mô tả trong phần thêm về quy định chức năng trong tài liệu Python.

Các *argssẽ cung cấp cho bạn tất cả các thông số chức năng như một tuple :

def foo(*args):
    for a in args:
        print(a)        

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

Các **kwargssẽ cung cấp cho bạn tất cả các đối số từ khóa trừ các đối tượng tương ứng với một tham số chính thức như một cuốn từ điển.

def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])  

bar(name='one', age=27)
# age 27
# name one

Cả hai thành ngữ có thể được trộn lẫn với các đối số bình thường để cho phép một tập hợp các đối số cố định và một số biến:

def foo(kind, *args, **kwargs):
   pass

Cũng có thể sử dụng cách này theo cách khác:

def foo(a, b, c):
    print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

Một cách sử dụng khác của *lthành ngữ là giải nén danh sách đối số khi gọi hàm.

def foo(bar, lee):
    print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

Trong Python 3 có thể sử dụng *lở phía bên trái của một bài tập ( Unpackable Unpacking ), mặc dù nó đưa ra một danh sách thay vì một tuple trong ngữ cảnh này:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Ngoài ra Python 3 thêm ngữ nghĩa mới (tham khảo PEP 3102 ):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
    pass

Hàm như vậy chỉ chấp nhận 3 đối số vị trí và mọi thứ sau đó *chỉ có thể được chuyển qua làm đối số từ khóa.


9
Đầu ra của [6] theo thứ tự ngược lại. tên một tuổi 27
thanos.a

54
@ thanos.a Python dicts, được sử dụng theo ngữ nghĩa để truyền đối số từ khóa, được sắp xếp tùy ý. Tuy nhiên, trong Python 3.6, các đối số từ khóa được đảm bảo ghi nhớ thứ tự chèn. "Thứ tự các phần tử **kwargsbây giờ tương ứng với thứ tự mà các đối số từ khóa được truyền cho hàm." - docs.python.org/3/whatsnew/3.6.html Trên thực tế, tất cả các ký tự trong CPython 3.6 sẽ ghi nhớ thứ tự chèn như một chi tiết triển khai, điều này trở thành tiêu chuẩn trong Python 3.7.
Aaron Hall

13
Rất chính xác, sạch sẽ, và dễ hiểu. Tôi đánh giá cao rằng bạn đã lưu ý rằng đó là "toán tử giải nén", do đó tôi có thể phân biệt với chuyển qua tham chiếu trong C. +1
bballdave025

Làm cách nào để kiểm tra chức năng cuối cùng với PEP 3102? Tôi gọi nó bằng func (1,2,3, name = "me", age = 10) và nó ném ngoại lệ:got an unexpected keyword argument 'name'
Kok How Teh

@KokHowTeh Bạn cần vượt qua các kwarg được đặt tên như chúng có trong hàm: func (1, 2, 3, kwarg1 = 'me', kwarg2 = 10)
John Aaron

622

Cũng đáng lưu ý rằng bạn có thể sử dụng ***khi gọi các chức năng là tốt. Đây là một phím tắt cho phép bạn truyền trực tiếp nhiều đối số cho một hàm bằng cách sử dụng danh sách / tuple hoặc từ điển. Ví dụ: nếu bạn có chức năng sau:

def foo(x,y,z):
    print("x=" + str(x))
    print("y=" + str(y))
    print("z=" + str(z))

Bạn có thể làm những việc như:

>>> mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3

>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3

>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3

Lưu ý: Các khóa trong mydictphải được đặt tên chính xác như các tham số của hàm foo. Nếu không nó sẽ ném một TypeError:

>>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
>>> foo(**mydict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'badnews'

175

Dấu * có nghĩa là có thể có bất kỳ số lượng đối số vị trí bổ sung nào. foo()Có thể được gọi như thế foo(1,2,3,4,5). Trong cơ thể của foo () param2 là một chuỗi chứa 2-5.

Double ** có nghĩa là có thể có bất kỳ số lượng tham số được đặt tên thêm. bar()Có thể được gọi như thế bar(1, a=2, b=3). Trong phần thân của thanh () param2 là một từ điển chứa {'a': 2, 'b': 3}

Với đoạn mã sau:

def foo(param1, *param2):
    print(param1)
    print(param2)

def bar(param1, **param2):
    print(param1)
    print(param2)

foo(1,2,3,4,5)
bar(1,a=2,b=3)

đầu ra là

1
(2, 3, 4, 5)
1
{'a': 2, 'b': 3}

5
Có lẽ một ví dụ bổ sung foobar(param1, *param2, **param3)là cần thiết cho sự hoàn chỉnh của câu trả lời này.
Aniket Thakur

1
@AniketThakur đã thêm phần còn lại ở đây .
Raj

148

**(sao đôi) và *(sao) làm cho các thông số

Chúng cho phép các hàm được xác định để chấp nhận và cho người dùng vượt qua bất kỳ số lượng đối số, vị trí ( *) và từ khóa ( **).

Hàm xác định

*argscho phép bất kỳ số lượng đối số vị trí tùy chọn (tham số), sẽ được gán cho một bộ dữ liệu có tên args.

**kwargscho phép bất kỳ số lượng đối số từ khóa tùy chọn (tham số), sẽ có trong một dict có tên kwargs.

Bạn có thể (và nên) chọn bất kỳ tên thích hợp nào, nhưng nếu ý định dành cho các đối số là về ngữ nghĩa không cụ thể argskwargslà tên chuẩn.

Mở rộng, Vượt qua bất kỳ số lượng đối số

Bạn cũng có thể sử dụng *args**kwargschuyển các tham số từ danh sách (hoặc bất kỳ lần lặp nào) và dicts (hoặc bất kỳ ánh xạ nào), tương ứng.

Hàm nhận các tham số không phải biết rằng chúng đang được mở rộng.

Ví dụ, xrange của Python 2 không mong đợi rõ ràng *args, nhưng vì nó lấy 3 số nguyên làm đối số:

>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x)    # expand here
xrange(0, 2, 2)

Một ví dụ khác, chúng ta có thể sử dụng mở rộng dict trong str.format:

>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'

Tính năng mới trong Python 3: Xác định hàm với các đối số chỉ từ khóa

Bạn có thể có các đối số chỉ từ khóa sau *args- ví dụ: ở đây, kwarg2phải được cung cấp dưới dạng đối số từ khóa - không phải theo vị trí:

def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): 
    return arg, kwarg, args, kwarg2, kwargs

Sử dụng:

>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})

Ngoài ra, *có thể được sử dụng bởi chính nó để chỉ ra rằng các đối số chỉ từ khóa theo sau, mà không cho phép các đối số vị trí không giới hạn.

def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): 
    return arg, kwarg, kwarg2, kwargs

Ở đây, kwarg2một lần nữa phải là một đối số từ khóa được đặt tên rõ ràng:

>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})

Và chúng tôi không còn có thể chấp nhận các đối số vị trí không giới hạn vì chúng tôi không có *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

Một lần nữa, đơn giản hơn, ở đây chúng tôi yêu cầu kwargđược cung cấp theo tên, không phải theo vị trí:

def bar(*, kwarg=None): 
    return kwarg

Trong ví dụ này, chúng tôi thấy rằng nếu chúng tôi cố gắng vượt qua kwargvị trí, chúng tôi sẽ gặp lỗi:

>>> bar('kwarg')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given

Chúng ta phải vượt qua kwargtham số một cách rõ ràng như một đối số từ khóa.

>>> bar(kwarg='kwarg')
'kwarg'

Bản demo tương thích Python 2

*args(thường nói "star-args") và **kwargs(các ngôi sao có thể được ngụ ý bằng cách nói "kwargs", nhưng phải rõ ràng với "kwargs sao đôi") là những thành ngữ phổ biến của Python khi sử dụng ký hiệu ***ký hiệu. Những tên biến cụ thể này không bắt buộc (ví dụ: bạn có thể sử dụng *foos**bars), nhưng việc rời khỏi quy ước có thể sẽ khiến các lập trình viên Python của bạn nổi giận.

Chúng ta thường sử dụng những thứ này khi chúng ta không biết chức năng của mình sẽ nhận được gì hoặc có bao nhiêu đối số chúng ta có thể vượt qua, và đôi khi ngay cả khi đặt tên riêng cho mỗi biến sẽ rất lộn xộn và dư thừa (nhưng đây là trường hợp thường rõ ràng tốt hơn là ngầm).

ví dụ 1

Hàm sau mô tả cách chúng có thể được sử dụng và thể hiện hành vi. Lưu ý bđối số được đặt tên sẽ được sử dụng bởi đối số vị trí thứ hai trước:

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))

Chúng tôi có thể kiểm tra trợ giúp trực tuyến để biết chữ ký của hàm, với help(foo), cho chúng tôi biết

foo(a, b=10, *args, **kwargs)

Hãy gọi hàm này với foo(1, 2, 3, 4, e=5, f=6, g=7)

bản in nào:

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}

Ví dụ 2

Chúng tôi cũng có thể gọi nó bằng cách sử dụng chức năng khác, trong đó chúng tôi chỉ cung cấp 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) in:

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}

Ví dụ 3: sử dụng thực tế trong trang trí

OK, vì vậy có lẽ chúng ta chưa thấy tiện ích này. Vì vậy, hãy tưởng tượng bạn có một số chức năng với mã dự phòng trước và / hoặc sau mã phân biệt. Các hàm được đặt tên sau đây chỉ là mã giả cho mục đích minh họa.

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

Chúng tôi có thể xử lý vấn đề này một cách khác nhau, nhưng chúng tôi chắc chắn có thể trích xuất sự dư thừa bằng một công cụ trang trí, và vì vậy ví dụ dưới đây của chúng tôi cho thấy cách thức *args**kwargscó thể rất hữu ích:

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

Và bây giờ mọi chức năng được bao bọc có thể được viết ngắn gọn hơn nhiều, vì chúng tôi đã phát hiện ra sự dư thừa:

@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)

Và bằng cách thanh toán ra mã của chúng tôi, trong đó *args**kwargscho phép chúng ta làm, chúng ta giảm dòng mã, cải thiện khả năng đọc và bảo trì, và có vị trí kinh điển duy nhất cho logic trong chương trình của chúng tôi. Nếu chúng ta cần thay đổi bất kỳ phần nào của cấu trúc này, chúng ta có một nơi để thực hiện mỗi thay đổi.


48

Trước tiên chúng ta hãy hiểu các đối số vị trí và đối số từ khóa là gì. Dưới đây là một ví dụ về định nghĩa hàm với các đối số Vị trí.

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(1,2,3)
#output:
1
2
3

Vì vậy, đây là một định nghĩa hàm với các đối số vị trí. Bạn cũng có thể gọi nó với các đối số từ khóa / được đặt tên:

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(a=1,b=2,c=3)
#output:
1
2
3

Bây giờ chúng ta hãy nghiên cứu một ví dụ về định nghĩa hàm với các đối số từ khóa :

def test(a=0,b=0,c=0):
     print(a)
     print(b)
     print(c)
     print('-------------------------')

test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------

Bạn cũng có thể gọi hàm này với các đối số vị trí:

def test(a=0,b=0,c=0):
    print(a)
    print(b)
    print(c)
    print('-------------------------')

test(1,2,3)
# output :
1
2
3
---------------------------------

Vì vậy, bây giờ chúng ta biết các định nghĩa hàm với các đối số từ khóa cũng như vị trí.

Bây giờ chúng ta hãy nghiên cứu toán tử '*' và toán tử '**'.

Xin lưu ý rằng các toán tử này có thể được sử dụng trong 2 lĩnh vực:

a) gọi hàm

b) định nghĩa hàm

Việc sử dụng toán tử '*' và toán tử '**' trong chức năng gọi.

Hãy để chúng tôi đi thẳng vào một ví dụ và sau đó thảo luận về nó.

def sum(a,b):  #receive args from function calls as sum(1,2) or sum(a=1,b=2)
    print(a+b)

my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}

# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple)   # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list)    # becomes same as sum(1,2) after unpacking my_list with  '*'
sum(**my_dict)   # becomes same as sum(a=1,b=2) after unpacking by '**' 

# output is 3 in all three calls to sum function.

Nhớ nhé

khi toán tử '*' hoặc '**' được sử dụng trong lệnh gọi hàm -

Toán tử '*' giải nén cấu trúc dữ liệu như danh sách hoặc chia thành các đối số cần thiết theo định nghĩa hàm.

Toán tử '**' giải nén một từ điển thành các đối số cần thiết theo định nghĩa hàm.

Bây giờ chúng ta hãy nghiên cứu sử dụng toán tử '*' trong định nghĩa hàm . Thí dụ:

def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
    sum = 0
    for a in args:
        sum+=a
    print(sum)

sum(1,2,3,4)  #positional args sent to function sum
#output:
10

Trong định nghĩa hàm , toán tử '*' gói các đối số nhận được thành một tuple.

Bây giờ chúng ta hãy xem một ví dụ về '**' được sử dụng trong định nghĩa hàm:

def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
    sum=0
    for k,v in args.items():
        sum+=v
    print(sum)

sum(a=1,b=2,c=3,d=4) #positional args sent to function sum

Trong định nghĩa hàm , toán tử '**' đóng gói các đối số nhận được vào một từ điển.

Hãy nhớ rằng:

Trong một hàm, gọi '*' giải nén cấu trúc dữ liệu của tuple hoặc liệt kê thành các đối số từ khóa hoặc vị trí để nhận được theo định nghĩa hàm.

Trong một hàm, gọi '**' giải nén cấu trúc dữ liệu của từ điển thành các đối số từ khóa hoặc vị trí được nhận theo định nghĩa hàm.

Trong một định nghĩa hàm , '*' gói các đối số vị trí thành một tuple.

Trong một định nghĩa hàm , '**' gói các đối số từ khóa vào một từ điển.


Thực sự sạch sẽ, từng bước và dễ dàng để làm theo lời giải thích!
Alexanderar

cảm ơn Hãy tiếp tục phát huy. [Ngoài ra để ghi chú thêm từ tôi, tôi đang ở @mrtechmaker trên twitter]
Karan Ahuja

32

Bảng này thuận tiện cho việc sử dụng ***trong chức năng xây dựnggọi hàm :

            In function construction         In function call
=======================================================================
          |  def f(*args):                 |  def f(a, b):
*args     |      for arg in args:          |      return a + b
          |          print(arg)            |  args = (1, 2)
          |  f(1, 2)                       |  f(*args)
----------|--------------------------------|---------------------------
          |  def f(a, b):                  |  def f(a, b):
**kwargs  |      return a + b              |      return a + b
          |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
          |      return f(**kwargs)        |  f(**kwargs)
          |  g(a=1, b=2)                   |
-----------------------------------------------------------------------

Điều này thực sự chỉ phục vụ để tóm tắt câu trả lời của Lorin Hochstein nhưng tôi thấy nó hữu ích.

Liên quan: việc sử dụng cho các toán tử star / splat đã được mở rộng trong Python 3


22

***có cách sử dụng đặc biệt trong danh sách đối số chức năng. * ngụ ý rằng đối số là một danh sách và **ngụ ý rằng đối số là một từ điển. Điều này cho phép các hàm lấy số lượng đối số tùy ý


17

Đối với những người bạn học bằng ví dụ!

  1. Mục đích của * là cung cấp cho bạn khả năng xác định hàm có thể lấy số lượng đối số tùy ý được cung cấp dưới dạng danh sách (ví dụ f(*myList)).
  2. Mục đích của **là cung cấp cho bạn khả năng cung cấp các đối số của hàm bằng cách cung cấp từ điển (ví dụ f(**{'x' : 1, 'y' : 2})).

Chúng ta hãy thể hiện điều này bằng cách định nghĩa một hàm mang theo hai biến bình thường x, yvà có thể chấp nhận nhiều đối số là myArgs, và có thể chấp nhận thậm chí nhiều đối số là myKW. Sau đó, chúng tôi sẽ chỉ cho bạn cách ysử dụng thức ăn myArgDict.

def f(x, y, *myArgs, **myKW):
    print("# x      = {}".format(x))
    print("# y      = {}".format(y))
    print("# myArgs = {}".format(myArgs))
    print("# myKW   = {}".format(myKW))
    print("# ----------------------------------------------------------------------")

# Define a list for demonstration purposes
myList    = ["Left", "Right", "Up", "Down"]
# Define a dictionary for demonstration purposes
myDict    = {"Wubba": "lubba", "Dub": "dub"}
# Define a dictionary to feed y
myArgDict = {'y': "Why?", 'y0': "Why not?", "q": "Here is a cue!"}

# The 1st elem of myList feeds y
f("myEx", *myList, **myDict)
# x      = myEx
# y      = Left
# myArgs = ('Right', 'Up', 'Down')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# y is matched and fed first
# The rest of myArgDict becomes additional arguments feeding myKW
f("myEx", **myArgDict)
# x      = myEx
# y      = Why?
# myArgs = ()
# myKW   = {'y0': 'Why not?', 'q': 'Here is a cue!'}
# ----------------------------------------------------------------------

# The rest of myArgDict becomes additional arguments feeding myArgs
f("myEx", *myArgDict)
# x      = myEx
# y      = y
# myArgs = ('y0', 'q')
# myKW   = {}
# ----------------------------------------------------------------------

# Feed extra arguments manually and append even more from my list
f("myEx", 4, 42, 420, *myList, *myDict, **myDict)
# x      = myEx
# y      = 4
# myArgs = (42, 420, 'Left', 'Right', 'Up', 'Down', 'Wubba', 'Dub')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# Without the stars, the entire provided list and dict become x, and y:
f(myList, myDict)
# x      = ['Left', 'Right', 'Up', 'Down']
# y      = {'Wubba': 'lubba', 'Dub': 'dub'}
# myArgs = ()
# myKW   = {}
# ----------------------------------------------------------------------

Hãy cẩn thận

  1. ** được dành riêng cho từ điển.
  2. Phân công đối số không tùy chọn xảy ra đầu tiên.
  3. Bạn không thể sử dụng một đối số không tùy chọn hai lần.
  4. Nếu áp dụng, **phải đến sau *, luôn luôn.

14

Từ tài liệu Python:

Nếu có nhiều đối số vị trí hơn so với các vị trí tham số chính thức, ngoại lệ TypeError được nêu ra, trừ khi có tham số chính thức sử dụng cú pháp "* định danh"; trong trường hợp này, tham số chính thức đó nhận được một tuple chứa các đối số vị trí thừa (hoặc một tuple trống nếu không có đối số vị trí thừa).

Nếu bất kỳ đối số từ khóa nào không tương ứng với tên tham số chính thức, ngoại lệ TypeError sẽ được nêu ra, trừ khi có tham số chính thức sử dụng cú pháp "** định danh"; trong trường hợp này, tham số chính thức đó nhận được một từ điển chứa các đối số từ khóa thừa (sử dụng từ khóa làm khóa và giá trị đối số làm giá trị tương ứng) hoặc từ điển trống (mới) nếu không có đối số từ khóa thừa.


10

* có nghĩa là nhận các đối số biến là tuple

** có nghĩa là nhận các đối số biến như từ điển

Được sử dụng như sau:

1) đơn *

def foo(*args):
    for arg in args:
        print(arg)

foo("two", 3)

Đầu ra:

two
3

2) Bây giờ **

def bar(**kwargs):
    for key in kwargs:
        print(key, kwargs[key])

bar(dic1="two", dic2=3)

Đầu ra:

dic1 two
dic2 3

8

Tôi muốn đưa ra một ví dụ mà những người khác chưa đề cập

* cũng có thể giải nén máy phát điện

Một ví dụ từ Tài liệu Python3

x = [1, 2, 3]
y = [4, 5, 6]

unzip_x, unzip_y = zip(*zip(x, y))

unzip_x sẽ là [1, 2, 3], unzip_y sẽ là [4, 5, 6]

Các zip () nhận được nhiều đối số iretable và trả về một trình tạo.

zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))

7

Trong Python 3.5, bạn cũng có thể sử dụng cú pháp này trong list, dict, tuple, và sethiển thị (cũng đôi khi được gọi literals). Xem PEP 488: Tổng quát hóa giải nén bổ sung .

>>> (0, *range(1, 4), 5, *range(6, 8))
(0, 1, 2, 3, 5, 6, 7)
>>> [0, *range(1, 4), 5, *range(6, 8)]
[0, 1, 2, 3, 5, 6, 7]
>>> {0, *range(1, 4), 5, *range(6, 8)}
{0, 1, 2, 3, 5, 6, 7}
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> e = {'six': 6, 'seven': 7}
>>> {'zero': 0, **d, 'five': 5, **e}
{'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}

Nó cũng cho phép nhiều lần lặp được giải nén trong một lệnh gọi hàm duy nhất.

>>> range(*[1, 10], *[2])
range(1, 10, 2)

(Cảm ơn mgilson cho liên kết PEP.)


1
Tôi không chắc chắn rằng đây là vi phạm "chỉ có một cách để làm điều đó". Không có cách nào khác để khởi tạo danh sách / bộ dữ liệu từ nhiều lần lặp - Hiện tại bạn cần xâu chuỗi chúng thành một lần lặp duy nhất không phải lúc nào cũng thuận tiện. Bạn có thể đọc về sự hợp lý trong PEP-0448 . Ngoài ra, đây không phải là tính năng python3.x, đây là tính năng python3.5 + :-).
mgilson

@mgilson, điều đó sẽ giải thích tại sao nó không được đề cập trước đó.
leewz

6

Ngoài các lệnh gọi hàm, * args và ** kwargs rất hữu ích trong hệ thống phân cấp lớp và cũng tránh phải viết __init__phương thức trong Python. Cách sử dụng tương tự có thể thấy trong các khung như mã Django.

Ví dụ,

def __init__(self, *args, **kwargs):
    for attribute_name, value in zip(self._expected_attributes, args):
        setattr(self, attribute_name, value)
        if kwargs.has_key(attribute_name):
            kwargs.pop(attribute_name)

    for attribute_name in kwargs.viewkeys():
        setattr(self, attribute_name, kwargs[attribute_name])

Một lớp con sau đó có thể là

class RetailItem(Item):
    _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin']

class FoodItem(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['expiry_date']

Lớp con sau đó được khởi tạo là

food_item = FoodItem(name = 'Jam', 
                     price = 12.0, 
                     category = 'Foods', 
                     country_of_origin = 'US', 
                     expiry_date = datetime.datetime.now())

Ngoài ra, một lớp con với một thuộc tính mới chỉ có ý nghĩa đối với thể hiện của lớp con đó có thể gọi lớp Cơ sở __init__để giảm tải cài đặt thuộc tính. Điều này được thực hiện thông qua * args và ** kwargs. kwargs chủ yếu được sử dụng để mã có thể đọc được bằng cách sử dụng các đối số được đặt tên. Ví dụ,

class ElectronicAccessories(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['specifications']
    # Depend on args and kwargs to populate the data as needed.
    def __init__(self, specifications = None, *args, **kwargs):
        self.specifications = specifications  # Rest of attributes will make sense to parent class.
        super(ElectronicAccessories, self).__init__(*args, **kwargs)

có thể được coi là

usb_key = ElectronicAccessories(name = 'Sandisk', 
                                price = '$6.00', 
                                category = 'Electronics',
                                country_of_origin = 'CN',
                                specifications = '4GB USB 2.0/USB 3.0')

Mã hoàn chỉnh ở đây


1
1. Về cơ bản init là một phương thức, vì vậy (trong bối cảnh này) nó không thực sự khác biệt. 2. Sử dụng # để lấy ý kiến, không phải """, mà chỉ đánh dấu chuỗi chữ 3. Sử dụng siêu nên là cách ưa thích, đặc biệt là ví dụ của bạn với đa cấp kế thừa..
0xc0de

4

Dựa trên câu trả lời của nickd ...

def foo(param1, *param2):
    print(param1)
    print(param2)


def bar(param1, **param2):
    print(param1)
    print(param2)


def three_params(param1, *param2, **param3):
    print(param1)
    print(param2)
    print(param3)


foo(1, 2, 3, 4, 5)
print("\n")
bar(1, a=2, b=3)
print("\n")
three_params(1, 2, 3, 4, s=5)

Đầu ra:

1
(2, 3, 4, 5)

1
{'a': 2, 'b': 3}

1
(2, 3, 4)
{'s': 5}

Về cơ bản, bất kỳ số lượng đối số vị trí nào cũng có thể sử dụng * args và bất kỳ đối số được đặt tên nào (hoặc kwargs aka đối số từ khóa) đều có thể sử dụng ** kwargs.


3

*args**kwargs: cho phép bạn truyền một số lượng đối số khác nhau cho hàm.

*args: được sử dụng để gửi danh sách đối số độ dài biến không từ khóa đến hàm:

def args(normal_arg, *argv):
    print("normal argument:", normal_arg)

    for arg in argv:
        print("Argument in list of arguments from *argv:", arg)

args('animals', 'fish', 'duck', 'bird')

Sẽ sản xuất:

normal argument: animals
Argument in list of arguments from *argv: fish
Argument in list of arguments from *argv: duck
Argument in list of arguments from *argv: bird

**kwargs*

**kwargscho phép bạn chuyển chiều dài biến đối số từ khóa cho hàm. Bạn nên sử dụng **kwargsnếu bạn muốn xử lý các đối số được đặt tên trong một hàm.

def who(**kwargs):
    if kwargs is not None:
        for key, value in kwargs.items():
            print("Your %s is %s." % (key, value))

who(name="Nikola", last_name="Tesla", birthday="7.10.1856", birthplace="Croatia")  

Sẽ sản xuất:

Your name is Nikola.
Your last_name is Tesla.
Your birthday is 7.10.1856.
Your birthplace is Croatia.

2

Ví dụ này sẽ giúp bạn nhớ *args, **kwargsvà thậm chí super, thừa kế bằng Python cùng một lúc.

class base(object):
    def __init__(self, base_param):
        self.base_param = base_param


class child1(base): # inherited from base class
    def __init__(self, child_param, *args) # *args for non-keyword args
        self.child_param = child_param
        super(child1, self).__init__(*args) # call __init__ of the base class and initialize it with a NON-KEYWORD arg

class child2(base):
    def __init__(self, child_param, **kwargs):
        self.child_param = child_param
        super(child2, self).__init__(**kwargs) # call __init__ of the base class and initialize it with a KEYWORD arg

c1 = child1(1,0)
c2 = child2(1,base_param=0)
print c1.base_param # 0
print c1.child_param # 1
print c2.base_param # 0
print c2.child_param # 1

1

Một ví dụ điển hình của việc sử dụng cả hai trong một hàm là:

>>> def foo(*arg,**kwargs):
...     print arg
...     print kwargs
>>>
>>> a = (1, 2, 3)
>>> b = {'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(*a,**b)
(1, 2, 3)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,**b) 
((1, 2, 3),)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,b) 
((1, 2, 3), {'aa': 11, 'bb': 22})
{}
>>>
>>>
>>> foo(a,*b)
((1, 2, 3), 'aa', 'bb')
{}

1

TL; DR

Dưới đây là 6 trường hợp sử dụng khác nhau cho ***trong lập trình python:

  1. Để chấp nhận bất kỳ số lượng đối số vị trí nào bằng cách sử dụng *args: def foo(*args): pass , ở đây foochấp nhận bất kỳ số lượng đối số vị trí nào, nghĩa là các cuộc gọi sau là hợp lệ foo(1),foo(1, 'bar')
  2. Để chấp nhận bất kỳ số lượng đối số từ khóa nào bằng cách sử dụng **kwargs: def foo(**kwargs): pass , ở đây 'foo' chấp nhận bất kỳ số lượng đối số từ khóa nào, nghĩa là các cuộc gọi sau là hợp lệ foo(name='Tom'),foo(name='Tom', age=33)
  3. Để chấp nhận bất kỳ số lượng đối số từ khóa và vị trí nào bằng cách sử dụng *args, **kwargs: def foo(*args, **kwargs): pass , ở đây foochấp nhận bất kỳ số lượng đối số từ khóa và vị trí nào, nghĩa là các cuộc gọi sau là hợp lệ foo(1,name='Tom'),foo(1, 'bar', name='Tom', age=33)
  4. Để thực thi các đối số chỉ từ khóa bằng cách sử dụng *: def foo(pos1, pos2, *, kwarg1): pass , ở đây *có nghĩa là foo chỉ chấp nhận các đối số từ khóa sau pos2, do đó foo(1, 2, 3)tăng TypeError nhưng foo(1, 2, kwarg1=3)vẫn ổn.
  5. Để thể hiện không quan tâm thêm đến các đối số vị trí hơn bằng cách sử dụng *_(Lưu ý: đây chỉ là quy ước): def foo(bar, baz, *_): pass có nghĩa là (theo quy ước) foochỉ sử dụng barbaz đối số trong hoạt động của nó và sẽ bỏ qua các đối số khác.
  6. Để thể hiện không quan tâm nhiều hơn đến các đối số từ khóa khác bằng cách sử dụng \**_(Lưu ý: đây chỉ là quy ước): def foo(bar, baz, **_): pass có nghĩa là (theo quy ước) foochỉ sử dụng barbazđối số trong hoạt động của nó và sẽ bỏ qua các đối số khác.

THƯỞNG: Từ python 3.8 trở đi, người ta có thể sử dụng /trong định nghĩa hàm để thực thi các tham số chỉ vị trí. Trong ví dụ sau, tham số a và b chỉ ở vị trí , trong khi c hoặc d có thể là vị trí hoặc từ khóa và e hoặc f được yêu cầu phải là từ khóa:

def f(a, b, /, c, d, *, e, f):
    pass

0

TL; DR

Nó đóng gói các đối số được truyền cho hàm vào listdicttương ứng bên trong thân hàm. Khi bạn xác định một chữ ký hàm như thế này:

def func(*args, **kwds):
    # do stuff

nó có thể được gọi với bất kỳ số lượng đối số và đối số từ khóa. Các đối số không phải từ khóa được đóng gói vào một danh sách được gọi argsbên trong thân hàm và các đối số từ khóa được đóng gói vào một lệnh được gọi kwdsbên trong thân hàm.

func("this", "is a list of", "non-keyowrd", "arguments", keyword="ligma", options=[1,2,3])

Bây giờ bên trong thân hàm, khi hàm được gọi, có hai biến cục bộ, args đó là một danh sách có giá trị ["this", "is a list of", "non-keyword", "arguments"]kwdsdictgiá trị có{"keyword" : "ligma", "options" : [1,2,3]}


Điều này cũng hoạt động ngược lại, tức là từ phía người gọi. ví dụ: nếu bạn có một hàm được định nghĩa là:

def f(a, b, c, d=1, e=10):
    # do stuff

bạn có thể gọi nó bằng cách giải nén iterables hoặc ánh xạ bạn có trong phạm vi gọi:

iterable = [1, 20, 500]
mapping = {"d" : 100, "e": 3}
f(*iterable, **mapping)
# That call is equivalent to
f(1, 20, 500, d=100, e=3)

0

Bối cảnh

  • trăn 3.x
  • giải nén với **
  • sử dụng với định dạng chuỗi

Sử dụng với định dạng chuỗi

Ngoài các câu trả lời trong chủ đề này, đây là một chi tiết khác không được đề cập ở nơi khác. Điều này mở rộng dựa trên câu trả lời của Brad Solomon

Giải nén với **cũng hữu ích khi sử dụng pythonstr.format .

Điều này hơi giống với những gì bạn có thể làm với python f-strings f-string nhưng với chi phí bổ sung là khai báo một dict để giữ các biến (chuỗi f không yêu cầu một dict).

Ví dụ nhanh

  ## init vars
  ddvars = dict()
  ddcalc = dict()
  pass
  ddvars['fname']     = 'Huomer'
  ddvars['lname']     = 'Huimpson'
  ddvars['motto']     = 'I love donuts!'
  ddvars['age']       = 33
  pass
  ddcalc['ydiff']     = 5
  ddcalc['ycalc']     = ddvars['age'] + ddcalc['ydiff']
  pass
  vdemo = []

  ## ********************
  ## single unpack supported in py 2.7
  vdemo.append('''
  Hello {fname} {lname}!

  Today you are {age} years old!

  We love your motto "{motto}" and we agree with you!
  '''.format(**ddvars)) 
  pass

  ## ********************
  ## multiple unpack supported in py 3.x
  vdemo.append('''
  Hello {fname} {lname}!

  In {ydiff} years you will be {ycalc} years old!
  '''.format(**ddvars,**ddcalc)) 
  pass

  ## ********************
  print(vdemo[-1])

-2
  • def foo(param1, *param2):là một phương thức có thể chấp nhận số lượng giá trị tùy ý cho *param2,
  • def bar(param1, **param2): là một phương thức có thể chấp nhận số lượng giá trị tùy ý bằng các khóa cho *param2
  • param1 là một tham số đơn giản.

Ví dụ, cú pháp để thực hiện các varargs trong Java như sau:

accessModifier methodName(datatype arg) {
    // method body
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.