Câu hỏi ** (sao đôi / dấu sao) và * (dấu sao / dấu hoa thị) làm gì cho các tham số?


Trong các định nghĩa phương pháp sau, * và ** làm cho param2?

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

1579
2017-08-31 15:04


gốc


Xem thêm stackoverflow.com/questions/6967632/… - Aaron Hall♦
Liên quan: Tại sao sử dụng đóng gói * args / ** kwargs thay vì chuyển danh sách / dict? - Steven Vascellaro


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


Các *args và **kwargs là một thành ngữ phổ biến cho phép số đối số tùy ý cho các hàm như được mô tả trong phần thêm về xác định hàm trong tài liệu Python.

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

In [1]: def foo(*args):
   ...:     for a in args:
   ...:         print a
   ...:         
   ...:         

In [2]: foo(1)
1


In [4]: foo(1,2,3)
1
2
3

Các **kwargs sẽ cung cấp cho bạn tất cả đối số từ khóa ngoại trừ những người tương ứng với thông số chính thức làm từ điển.

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

In [6]: 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

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

In [9]: def foo(bar, lee):
   ...:     print bar, lee
   ...:     
   ...:     

In [10]: l = [1,2]

In [11]: foo(*l)
1 2

Trong Python 3 có thể sử dụng *l ở bên trái của một bài tập (Mở rộng Iterable 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 này chỉ chấp nhận 3 đối số vị trí và mọi thứ sau * chỉ có thể được chuyển dưới dạng đối số từ khóa.


1555
2017-08-31 15:17



Đầu ra của [6] theo thứ tự ngược lại. tên một tuổi 27 - thanos.a
@ thanos.a Python dicts, ngữ nghĩa được sử dụng cho các đối số từ khóa đi qua, được tùy ý ra lệnh. 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ủa các phần tử trong **kwargs bây giờ tương ứng với thứ tự mà các đối số từ khóa được chuyển đến hàm. "- docs.python.org/3/whatsnew/3.6.html Trong thực tế, tất cả các dicts trong CPython 3.6 sẽ nhớ thứ tự chèn, nhưng đây là một chi tiết thực hiện cho bây giờ, và người dùng không nên dựa vào nó. - Aaron Hall♦
"Các **kwargs Tôi có hiểu chính xác rằng các tham số chính thức được bổ sung cho các đối số từ khóa, cùng nhau làm cho tất cả các đầu vào thành một hàm không? - Post169
Rất chính xác, rõ ràng 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", để tôi có thể phân biệt được việc chuyển qua tham chiếu trong C. +1 - bballdave025


Nó cũng đáng chú ý là bạn có thể sử dụng * và ** khi gọi các chức năng. Đây là một phím tắt cho phép bạn chuyển nhiều đối số đến một hàm trực tiếp bằng cách sử dụng danh sách / bộ tuple hoặc từ điển. Ví dụ, nếu bạn có hàm 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 phím trong mydict phả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'

451
2017-08-31 15:47



Bạn nên chỉ ra, rằng các phím trong mydict phải được đặt tên chính xác như các tham số của foo. Nếu không nó sẽ ném một typeError. - winklerrr
Điều này không trả lời câu hỏi. Trong định dạng này, nó phải là một bình luận. - styrofoam fly
@styrofoamfly Có vẻ như khối mã như vậy không thể khớp trong một nhận xét. - MeadowMuffins
@MeadowMuffins vì vậy chúng tôi đã có một văn bản dài không phải là câu trả lời ... - styrofoam fly
@styrofoamfly Đó là xứng đáng biết là tốt, khi nhìn thấy câu hỏi. Nó sẽ tốt hơn là một phụ lục cho câu trả lời bình chọn cao nhất, mặc dù vi phạm các quy tắc của SO. - MeadowMuffins


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

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

Với 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}

128
2017-08-31 15:20





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

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

Định nghĩa hàm

*args cho phép đối với bất kỳ số đối số vị trí tùy chọn nào (tham số), sẽ được gán cho một bộ tuple có tên args.

**kwargs cho phép đối với bất kỳ số đối số từ khóa tùy chọn nào (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 cho các đối số là các ngữ nghĩa không cụ thể, args và kwargs là tên chuẩn.

Mở rộng, Chuyển bất kỳ số lượng đối số nào

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

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

Ví dụ, xrange của Python 2 không rõ ràng mong đợi *args, nhưng vì phải mất 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 tôi 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'

Mới trong Python 3: Định nghĩa các hàm chỉ với các đối số từ khóa

Bạn có thể có đối số chỉ từ khóa sau *args - ví dụ, ở đây, kwarg2 phải được đưa ra dưới dạng đối số từ khóa - không phải là 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'})

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

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

Đây, kwarg2 mộ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 thể chấp nhận các đối số vị trí không giới hạn bởi 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 đặt theo tên, chứ không phải theo vị trí:

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

Trong ví dụ này, chúng ta thấy rằng nếu chúng ta cố gắng vượt qua kwarg về mặt địa lý, chúng tôi 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 một cách rõ ràng kwarg tham số làm đối số từ khóa.

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

Bản trình diễn tương thích với Python 2

*args (thường được gọi là "star-args") và **kwargs (các ngôi sao có thể được ngụ ý bằng cách nói "kwargs", nhưng rõ ràng với "kwargs hai sao") là các thành ngữ phổ biến của Python để sử dụng * và ** ký hiệu. Các tên biến cụ thể này không bắt buộc (ví dụ: bạn có thể sử dụng *foos và **bars), nhưng một khởi hành từ hội nghị có khả năng kích thích các lập trình viên Python đồng nghiệp của bạn.

Chúng ta thường sử dụng chúng khi chúng ta không biết hàm của chúng ta sẽ nhận được hoặc có bao nhiêu đối số chúng ta có thể truyền đi, và đôi khi ngay cả khi đặt tên cho mỗi biến riêng biệt sẽ rất lộn xộn và dư thừa (nhưng đây là trường hợp thường rõ ràng là 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 ý tên b đối số sẽ được tiêu thụ 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 cho 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 bằng foo(1, 2, 3, 4, e=5, f=6, g=7) 

mà in:

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 một chức năng khác, mà 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) bản 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 tôi chưa thấy tiện ích. 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 khi 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 ta có thể xử lý điều này một cách khác nhau, nhưng chúng ta chắc chắn có thể trích xuất dự phòng với một trang trí, và vì vậy ví dụ dưới đây của chúng tôi minh họa cách *args và **kwargs có 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à giờ đây, mọi chức năng được bao bọc có thể được viết gọn gàng hơn nhiều, vì chúng tôi đã tính toán 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 tính toán mã của chúng tôi, *args và **kwargs cho phép chúng tôi làm, chúng tôi giảm các dòng mã, cải thiện khả năng đọc và bảo trì, và có cá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 phải 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 từng thay đổi.


107
2017-10-14 16:34





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

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

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

Đây là định nghĩa hàm với các đối số vị trí. Bạn cũng có thể gọi nó bằng 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 đố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
---------------------------------

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 ý các nhà khai thác này có thể được sử dụng trong 2 lĩnh vực:

a) chức năng gọi

b) chức năng độ nét

Sử dụng toán tử '*' và toán tử '**' trong gọi hàm. 

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.

Hãy nhớ

khi toán tử '*' hoặc '**' được sử dụng trong chức năng gọi - -

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

Toán tử '**' đưa một từ điển vào 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 chức năng độ nét. 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 chức năng Định nghĩa toán tử '*' gói các đối số đã nhận vào một bộ 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 chức năng Định nghĩa Toán tử '**' gói các đối số đã nhận vào một từ điển.

Vì vậy, hãy nhớ:

Trong một chức năng gọi các '*' ba lô cấu trúc dữ liệu của tuple hoặc liệt kê vào các đối số vị trí hoặc từ khóa để nhận được bởi định nghĩa hàm.

Trong một chức năng gọi các '**' ba lô cấu trúc dữ liệu của từ điển thành các đối số vị trí hoặc từ khóa được nhận bởi định nghĩa hàm.

Trong một chức năng độ nét các '*' gói các đối số vị trí thành một tuple.

Trong một chức năng độ nét các '**' gói đối số từ khóa vào từ điển.


37
2018-01-20 11:40





* và ** có cách sử dụng đặc biệt trong danh sách đối số hàm. * 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 có số lượng tùy ý lập luận


20
2017-09-11 04:33





Từ tài liệu Python:

Nếu có nhiều đối số vị trí hơn là có các khe tham số chính thức, ngoại lệ TypeError được nâng lên, trừ khi một tham số chính thức sử dụng cú pháp "* số nhận dạng" có mặt; 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í dư thừa (hoặc một bộ trống rỗng nếu không có các đối số vị trí dư thừa).

Nếu bất kỳ đối số từ khóa nào không tương ứng với tên thông số chính thức, ngoại lệ TypeError được nâng lên, trừ khi một tham số chính thức sử dụng cú pháp "** số nhận dạng" có mặt; trong trường hợp này, tham số chính thức nhận từ điển chứa các đối số từ khóa dư thừa (sử dụng các 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 dư thừa.


11
2017-08-31 15:07