Mục đích và việc sử dụng ** kwargs là gì?


763

Việc sử dụng **kwargstrong Python là gì?

Tôi biết bạn có thể làm một cái objects.filtertrên bàn và vượt qua trong một **kwargscuộc tranh cãi.  

Tôi cũng có thể làm điều này để chỉ định thời gian deltas tức là timedelta(hours = time1)?

Làm thế nào chính xác nó hoạt động? Có phải các lớp là 'giải nén'? Giống như a,b=1,2?


27
Nếu bạn gặp phải câu hỏi này như tôi, hãy xem thêm: * args và ** kwargs?
sumid

3
Một lời giải thích ngắn gọn đáng chú ý ở đây : "* thu thập tất cả các đối số vị trí trong một tuple", "** thu thập tất cả các đối số từ khóa trong từ điển". Từ khóa là thu thập .
osa

24
Chỉ FYI: kwargslà viết tắt của KeyWord ARGumentS, tức là các đối số đã đặt khóa
Richard de Wit

Câu trả lời:


868

Bạn có thể sử dụng **kwargsđể cho phép các hàm của bạn có số lượng đối số từ khóa tùy ý ("kwargs" có nghĩa là "đối số từ khóa"):

>>> def print_keyword_args(**kwargs):
...     # kwargs is a dict of the keyword args passed to the function
...     for key, value in kwargs.iteritems():
...         print "%s = %s" % (key, value)
... 
>>> print_keyword_args(first_name="John", last_name="Doe")
first_name = John
last_name = Doe

Bạn cũng có thể sử dụng **kwargscú pháp khi gọi các hàm bằng cách xây dựng một từ điển các đối số từ khóa và chuyển nó đến hàm của bạn:

>>> kwargs = {'first_name': 'Bobby', 'last_name': 'Smith'}
>>> print_keyword_args(**kwargs)
first_name = Bobby
last_name = Smith

Các Bài chỉ dẫn Python chứa một lời giải thích tốt về cách hoạt động, cùng với một số ví dụ tốt đẹp.

<- Cập nhật ->

Đối với những người sử dụng Python 3, thay vì iteritems (), hãy sử dụng các mục ()


1
@ yashas123 Không; nếu bạn lặp đi lặp lại một cái gì đó trống rỗng, không có gì xảy ra, vì vậy bất kỳ mã nào có thể đến tiếp theo sẽ chạy bình thường.
JG

330

Giải nén từ điển

** giải nén từ điển.

Điều này

func(a=1, b=2, c=3)

giống như

args = {'a': 1, 'b': 2, 'c':3}
func(**args)

Nó hữu ích nếu bạn phải xây dựng các tham số:

args = {'name': person.name}
if hasattr(person, "address"):
    args["address"] = person.address
func(**args)  # either expanded to func(name=person.name) or
              #                    func(name=person.name, address=person.address)

Thông số đóng gói của hàm

def setstyle(**styles):
    for key, value in styles.iteritems():      # styles is a regular dictionary
        setattr(someobject, key, value)

Điều này cho phép bạn sử dụng chức năng như thế này:

setstyle(color="red", bold=False)

13
kwarg chỉ là một tên biến phải không? vì vậy tôi có thể sử dụng def func (** args): và nó có hoạt động không?
Sriram

11
@Sriram: Phải. Các dấu hoa thị rất quan trọng. kwargs chỉ là cái tên được đặt cho nó nếu không có gì tốt hơn. (Thường có.)
Georg Schölly

54
@Sriram: vì dễ đọc, bạn nên bám vào kwargs - các lập trình viên khác sẽ đánh giá cao nó.
johndodo

12
** do unpack dictionaries.>> tâm trí bị thổi bay / dĩ nhiên! +1 để giải thích bit đó.
Marc

13
Lưu ý: .iteritems()đã được đổi tên thành .items()Python 3.
fnkr

67

kwargs chỉ là một từ điển được thêm vào các tham số.

Một từ điển có thể chứa các cặp khóa, giá trị. Và đó là kwargs. Ok, đây là cách.

Những gì cho không đơn giản như vậy.

Ví dụ (rất giả thuyết) bạn có một giao diện chỉ gọi các thói quen khác để thực hiện công việc:

def myDo(what, where, why):
   if what == 'swim':
      doSwim(where, why)
   elif what == 'walk':
      doWalk(where, why)
   ...

Bây giờ bạn có một "ổ" phương thức mới:

elif what == 'drive':
   doDrive(where, why, vehicle)

Nhưng hãy đợi một phút, có một "phương tiện" mới - bạn không biết nó trước đây. Bây giờ bạn phải thêm nó vào chữ ký của hàm myDo.

Tại đây bạn có thể ném kwargs vào chơi - bạn chỉ cần thêm kwargs vào chữ ký:

def myDo(what, where, why, **kwargs):
   if what == 'drive':
      doDrive(where, why, **kwargs)
   elif what == 'swim':
      doSwim(where, why, **kwargs)

Bằng cách này, bạn không cần thay đổi chữ ký của chức năng giao diện mỗi khi một số thói quen được gọi của bạn có thể thay đổi.

Đây chỉ là một ví dụ hay mà bạn có thể thấy kwargs hữu ích.


46

Trên cơ sở một mẫu tốt đôi khi tốt hơn một bài diễn văn dài, tôi sẽ viết hai hàm bằng cách sử dụng tất cả các phương tiện truyền đối số biến python (cả đối số vị trí và tên được đặt tên). Bạn có thể dễ dàng có thể nhìn thấy những gì nó làm một mình:

def f(a = 0, *args, **kwargs):
    print("Received by f(a, *args, **kwargs)")
    print("=> f(a=%s, args=%s, kwargs=%s" % (a, args, kwargs))
    print("Calling g(10, 11, 12, *args, d = 13, e = 14, **kwargs)")
    g(10, 11, 12, *args, d = 13, e = 14, **kwargs)

def g(f, g = 0, *args, **kwargs):
    print("Received by g(f, g = 0, *args, **kwargs)")
    print("=> g(f=%s, g=%s, args=%s, kwargs=%s)" % (f, g, args, kwargs))

print("Calling f(1, 2, 3, 4, b = 5, c = 6)")
f(1, 2, 3, 4, b = 5, c = 6)

Và đây là đầu ra:

Calling f(1, 2, 3, 4, b = 5, c = 6)
Received by f(a, *args, **kwargs) 
=> f(a=1, args=(2, 3, 4), kwargs={'c': 6, 'b': 5}
Calling g(10, 11, 12, *args, d = 13, e = 14, **kwargs)
Received by g(f, g = 0, *args, **kwargs)
=> g(f=10, g=11, args=(12, 2, 3, 4), kwargs={'c': 6, 'b': 5, 'e': 14, 'd': 13})

28

Motif: *args**kwargsphục vụ như một trình giữ chỗ cho các đối số cần được truyền cho một lệnh gọi hàm

sử dụng *args**kwargsgọi hàm

def args_kwargs_test(arg1, arg2, arg3):
    print "arg1:", arg1
    print "arg2:", arg2
    print "arg3:", arg3

Bây giờ chúng ta sẽ sử dụng *argsđể gọi hàm được xác định ở trên

#args can either be a "list" or "tuple"
>>> args = ("two", 3, 5)  
>>> args_kwargs_test(*args)

kết quả:

arg1: hai
arg2: 3
arg3: 5


Bây giờ, sử dụng **kwargsđể gọi cùng chức năng

#keyword argument "kwargs" has to be a dictionary
>>> kwargs = {"arg3":3, "arg2":'two', "arg1":5}
>>> args_kwargs_test(**kwargs)

kết quả:

arg1: 5
arg2: hai
arg3: 3

Điểm mấu chốt: *argskhông có trí thông minh, nó chỉ đơn giản là nội suy các đối số đã truyền cho các tham số (theo thứ tự từ trái sang phải) trong khi **kwargshành xử thông minh bằng cách đặt giá trị thích hợp @ vị trí bắt buộc


24
  • kwargstrong **kwargschỉ là tên biến. Bạn rất có thể có**anyVariableName
  • kwargslà viết tắt của "đối số từ khóa". Nhưng tôi cảm thấy tốt hơn nên gọi chúng là "đối số có tên", vì đây là những đối số đơn giản được truyền cùng với tên (tôi không tìm thấy bất kỳ ý nghĩa nào đối với từ "từ khóa" trong thuật ngữ "đối số từ khóa". Tôi đoán "từ khóa" thường có nghĩa là các từ được dành riêng bởi ngôn ngữ lập trình và do đó không được lập trình viên sử dụng cho các tên biến. Không có điều đó xảy ra ở đây trong trường hợp kwargs.). Vì vậy, chúng tôi đặt tên param1param2cho hai giá trị tham số được truyền cho hàm như sau : func(param1="val1",param2="val2"), thay vì chỉ truyền các giá trị : func(val1,val2). Vì vậy, tôi cảm thấy chúng nên được gọi một cách thích hợp là "số lượng đối số được đặt tên tùy ý" vì chúng ta có thể chỉ định bất kỳ số lượng nào của các tham số này (nghĩa làfuncfunc(**kwargs)

Vì vậy, được nói rằng hãy để tôi giải thích "đối số được đặt tên" trước và sau đó là "số lượng đối số được đặt tên tùy ý" kwargs.

Đối số được đặt tên

  • args được đặt tên nên theo args vị trí
  • thứ tự của args được đặt tên là không quan trọng
  • Thí dụ

    def function1(param1,param2="arg2",param3="arg3"):
        print("\n"+str(param1)+" "+str(param2)+" "+str(param3)+"\n")
    
    function1(1)                      #1 arg2 arg3   #1 positional arg
    function1(param1=1)               #1 arg2 arg3   #1 named arg
    function1(1,param2=2)             #1 2 arg3      #1 positional arg, 1 named arg
    function1(param1=1,param2=2)      #1 2 arg3      #2 named args       
    function1(param2=2, param1=1)     #1 2 arg3      #2 named args out of order
    function1(1, param3=3, param2=2)  #1 2 3         #
    
    #function1()                      #invalid: required argument missing
    #function1(param2=2,1)            #invalid: SyntaxError: non-keyword arg after keyword arg
    #function1(1,param1=11)           #invalid: TypeError: function1() got multiple values for argument 'param1'
    #function1(param4=4)              #invalid: TypeError: function1() got an unexpected keyword argument 'param4'

Số lượng đối số được đặt tên tùy ý kwargs

  • Trình tự các tham số chức năng:
    1. tham số vị trí
    2. tham số chính thức nắm bắt số lượng đối số tùy ý (tiền tố là *)
    3. đặt tên tham số chính thức
    4. tham số chính thức nắm bắt số lượng tham số được đặt tên tùy ý (tiền tố là **)
  • Thí dụ

    def function2(param1, *tupleParams, param2, param3, **dictionaryParams):
        print("param1: "+ param1)
        print("param2: "+ param2)
        print("param3: "+ param3)
        print("custom tuple params","-"*10)
        for p in tupleParams:
            print(str(p) + ",")
        print("custom named params","-"*10)
        for k,v in dictionaryParams.items():
            print(str(k)+":"+str(v))
    
    function2("arg1",
              "custom param1",
              "custom param2",
              "custom param3",
              param3="arg3",
              param2="arg2", 
              customNamedParam1 = "val1",
              customNamedParam2 = "val2"
              )
    
    # Output
    #
    #param1: arg1
    #param2: arg2
    #param3: arg3
    #custom tuple params ----------
    #custom param1,
    #custom param2,
    #custom param3,
    #custom named params ----------
    #customNamedParam2:val2
    #customNamedParam1:val1

Truyền các biến tuple và dict cho các đối số tùy chỉnh

Để hoàn thành nó, tôi cũng lưu ý rằng chúng ta có thể vượt qua

  • "tham số chính thức nắm bắt số lượng đối số tùy ý" dưới dạng biến tuple và
  • "tham số chính thức bắt số lượng tham số được đặt tên tùy ý" dưới dạng biến dict

Do đó, cuộc gọi tương tự ở trên có thể được thực hiện như sau:

tupleCustomArgs = ("custom param1", "custom param2", "custom param3")
dictCustomNamedArgs = {"customNamedParam1":"val1", "customNamedParam2":"val2"}

function2("arg1",
      *tupleCustomArgs,    #note *
      param3="arg3",
      param2="arg2", 
      **dictCustomNamedArgs     #note **
      )

Cuối cùng lưu ý ***trong các cuộc gọi chức năng ở trên. Nếu chúng tôi bỏ qua chúng, chúng tôi có thể nhận được kết quả xấu.

Bỏ qua *trong tuple args:

function2("arg1",
      tupleCustomArgs,   #omitting *
      param3="arg3",
      param2="arg2", 
      **dictCustomNamedArgs
      )

in

param1: arg1
param2: arg2
param3: arg3
custom tuple params ----------
('custom param1', 'custom param2', 'custom param3'),
custom named params ----------
customNamedParam2:val2
customNamedParam1:val1

Trên tuple ('custom param1', 'custom param2', 'custom param3')được in như là.

Bỏ qua lập luận dict:

function2("arg1",
      *tupleCustomArgs,   
      param3="arg3",
      param2="arg2", 
      dictCustomNamedArgs   #omitting **
      )

cho

dictCustomNamedArgs
         ^
SyntaxError: non-keyword arg after keyword arg

3
Tôi sẽ tưởng tượng keywordthuật ngữ này xuất phát từ thực tế bạn đang chuyển qua một lệnh chính là cơ sở dữ liệu của các cặp khóa-giá trị.
crobar

bạn có nghĩa là từ "khóa" trong cặp "giá trị"? Ngoài ra nó thường không được gọi là cơ sở dữ liệu, nhưng từ điển. Nhưng vẫn không thể tìm thấy bất kỳ ý nghĩa đối với việc sử dụng từ "từ khóa".
Mahesha999

9

Ngoài ra, bạn cũng có thể kết hợp các cách sử dụng khác nhau khi gọi các hàm kwargs:

def test(**kwargs):
    print kwargs['a']
    print kwargs['b']
    print kwargs['c']


args = { 'b': 2, 'c': 3}

test( a=1, **args )

đưa ra kết quả này:

1
2
3

Lưu ý rằng ** kwargs phải là đối số cuối cùng


5

kwargs là một đường cú pháp để truyền các đối số tên là từ điển (cho func) hoặc từ điển như các đối số được đặt tên (để func)


5

Đây là một chức năng đơn giản phục vụ để giải thích việc sử dụng:

def print_wrap(arg1, *args, **kwargs):
    print(arg1)
    print(args)
    print(kwargs)
    print(arg1, *args, **kwargs)

Bất kỳ đối số nào không được chỉ định trong định nghĩa hàm sẽ được đưa vào argsdanh sách hoặc kwargsdanh sách, tùy thuộc vào việc chúng có phải là đối số từ khóa hay không:

>>> print_wrap('one', 'two', 'three', end='blah', sep='--')
one
('two', 'three')
{'end': 'blah', 'sep': '--'}
one--two--threeblah

Nếu bạn thêm một đối số từ khóa không bao giờ được chuyển đến một hàm, một lỗi sẽ xuất hiện:

>>> print_wrap('blah', dead_arg='anything')
TypeError: 'dead_arg' is an invalid keyword argument for this function

1

Đây là một ví dụ mà tôi hy vọng là hữu ích:

#! /usr/bin/env python
#
def g( **kwargs) :
  print ( "In g ready to print kwargs" )
  print kwargs
  print ( "in g, calling f")
  f ( **kwargs )
  print ( "In g, after returning from f")

def f( **kwargs ) :
  print ( "in f, printing kwargs")
  print ( kwargs )
  print ( "In f, after printing kwargs")


g( a="red", b=5, c="Nassau")

g( q="purple", w="W", c="Charlie", d=[4, 3, 6] )

Khi bạn chạy chương trình, bạn nhận được:

$ python kwargs_demo.py 
In g ready to print kwargs
{'a': 'red', 'c': 'Nassau', 'b': 5}
in g, calling f
in f, printing kwargs
{'a': 'red', 'c': 'Nassau', 'b': 5}
In f, after printing kwargs
In g, after returning from f
In g ready to print kwargs
{'q': 'purple', 'c': 'Charlie', 'd': [4, 3, 6], 'w': 'W'}
in g, calling f
in f, printing kwargs
{'q': 'purple', 'c': 'Charlie', 'd': [4, 3, 6], 'w': 'W'}
In f, after printing kwargs
In g, after returning from f

Chìa khóa lấy đi ở đây là số lượng đối số được đặt tên trong cuộc gọi dịch vào một từ điển trong hàm.


0

Đây là ví dụ đơn giản để hiểu về giải nén python ,

>>> def f(*args, **kwargs):
...    print 'args', args, 'kwargs', kwargs

vd

>>>f(1, 2)
>>> args (1,2) kwargs {} #args return parameter without reference as a tuple
>>>f(a = 1, b = 2)
>>> args () kwargs {'a': 1, 'b': 2} #args is empty tuple and kwargs return parameter with reference as a dictionary

0

Trong Java, bạn sử dụng các hàm tạo để nạp chồng các lớp và cho phép nhiều tham số đầu vào. Trong python, bạn có thể sử dụng kwargs để cung cấp hành vi tương tự.

ví dụ java: https://beginnersbook.com/2013/05/constructor-overloading/

ví dụ trăn:

class Robot():
    # name is an arg and color is a kwarg
    def __init__(self,name, color='red'):
        self.name = name
        self.color = color

red_robot = Robot('Bob')
blue_robot = Robot('Bob', color='blue')

print("I am a {color} robot named {name}.".format(color=red_robot.color, name=red_robot.name))
print("I am a {color} robot named {name}.".format(color=blue_robot.color, name=blue_robot.name))

>>> I am a red robot named Bob.
>>> I am a blue robot named Bob.

chỉ là một cách khác để suy nghĩ về nó.


0

Đối số từ khóa thường được rút ngắn thành kwargs trong Python. Trong lập trình máy tính ,

đối số từ khóa đề cập đến sự hỗ trợ của ngôn ngữ máy tính cho các lệnh gọi hàm trong đó nêu rõ tên của từng tham số trong lệnh gọi hàm.

Việc sử dụng hai dấu sao trước tên tham số, ** kwargs , là khi người ta không biết có bao nhiêu đối số từ khóa sẽ được truyền vào hàm. Khi đó, nó được gọi là Đối số từ khóa Arbitrary / Wildcard.

Một ví dụ về điều này là các chức năng thu của Django .

def my_callback(sender, **kwargs):
    print("Request finished!")

Lưu ý rằng hàm có một đối số người gửi, cùng với các đối số từ khóa ký tự đại diện (** kwargs); tất cả các bộ xử lý tín hiệu phải có các đối số này. Tất cả các tín hiệu gửi đối số từ khóa và có thể thay đổi các đối số từ khóa đó bất cứ lúc nào. Trong trường hợp request_finished , tài liệu này được gửi là không gửi đối số, điều đó có nghĩa là chúng tôi có thể bị cám dỗ để viết xử lý tín hiệu của chúng tôi dưới dạng my_callback (người gửi).

Điều này sẽ sai - trên thực tế, Django sẽ đưa ra lỗi nếu bạn làm như vậy. Đó là bởi vì tại bất kỳ điểm nào, các đối số có thể được thêm vào tín hiệu và người nhận của bạn phải có khả năng xử lý các đối số mới đó.

Lưu ý rằng nó không phải được gọi là kwargs , nhưng nó cần phải có ** (tên kwargs là một quy ước).

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.