ส่งอาร์กิวเมนต์ ** kwargs ไปยังฟังก์ชันอื่นด้วย ** kwargs


152

ฉันไม่เข้าใจตัวอย่างต่อไปนี้สมมติว่าฉันมีฟังก์ชั่นเหล่านี้:

# python likes
def save(filename, data, **kwargs):
    fo = openX(filename, "w", **kwargs) # <- #1
    fo.write(data)
    fo.close()
# python doesnt like
def save2(filename, data, **kwargs):
    fo = openX(filename, "w", kwargs) # <- #2
    fo.write(data)
    fo.close()

def openX(filename, mode, **kwargs):
    #doing something fancy and returning a file object

ทำไม # 1 เป็นทางออกที่ถูกต้องและ # 2 ผิด? **kwargsโดยพื้นฐานแล้วมันคือ dict ดังนั้นหากฉันต้องการส่งผ่านข้อโต้แย้งไปยัง openX ฉันคิดว่าวิธีที่ถูกต้องก็คือถ้าไม่มี**และให้แค่ dict แต่หลามเห็นได้ชัดว่าไม่ชอบสิ่งที่สองและบอกฉันฉันให้ 3 แทน 2 ข้อโต้แย้ง ดังนั้นเหตุผลเบื้องหลังนี้คืออะไร

python 

5
ฉันสงสัยว่าทำไมคุณเรียกมัน**argsในรหัส นี่อาจเป็นชื่อที่แย่ที่สุดที่ผู้คนจะสับสนกับมัน*args
John La Rooy

1
ฉันไม่เคยใช้ * args ดังนั้นฉันจึงใช้ ** args ^^ แต่ก็สามารถแก้ไขได้

คำตอบ:


155

ในตัวอย่างที่สองคุณมีอาร์กิวเมนต์ 3 ตัว ได้แก่ ชื่อไฟล์โหมดและพจนานุกรม ( kwargs) แต่ Python คาดว่าจะมี 2 ข้อโต้แย้งอย่างเป็นทางการและข้อโต้แย้งคำหลัก

เมื่อนำหน้าพจนานุกรมด้วย '**' คุณจะนำพจนานุกรมออกจากกล่องkwargsไปที่อาร์กิวเมนต์ของคำหลัก

พจนานุกรม (ชนิดdict) เป็นตัวแปรเดียวที่มีคู่ค่าคีย์

"อาร์กิวเมนต์คำหลัก" เป็นวิธีการค่าคีย์พารามิเตอร์

พจนานุกรมใด ๆ สามารถแยกออกจากอาร์กิวเมนต์ของคำหลักโดยนำหน้าด้วย**ระหว่างการเรียกฟังก์ชัน


5
ตอนนี้ฉันเข้าใจแล้ว ฉันคิดว่าคำหลักและ dict นั้นเหมือนกัน

13
"พจนานุกรมใด ๆ ก็ได้โดยขยายเป็นคำหลักโดยนำหน้าด้วย ** ระหว่างการเรียกใช้ฟังก์ชัน" <- เยี่ยมเลย

1
ดูเพิ่มเติมที่: Python เอกสารเกี่ยวกับพารามิเตอร์คำหลักและรายการอาร์กิวเมนต์ที่เปิดออก
ไดโนเสาร์

8
ตัวอย่างรหัสจริงจะทำให้คำตอบนี้ชัดเจนขึ้น
OrangeDog

13

**ไวยากรณ์บอกหลามข้อโต้แย้งคำหลักที่เก็บลงในพจนานุกรม save2จะผ่านมันลงเป็นอาร์กิวเมนต์ไม่ใช่คำหลัก (วัตถุพจนานุกรม) openXจะไม่เห็นข้อโต้แย้งคำหลักใด ๆ ดังนั้น**argsไม่ได้ใช้ แทนที่จะได้รับอาร์กิวเมนต์ที่ไม่ใช่คำหลักลำดับที่สาม (พจนานุกรม) เพื่อแก้ไขที่เปลี่ยนนิยามของopenXฟังก์ชั่น

def openX(filename, mode, kwargs):
    pass

ขอบคุณ แต่ฉันยังต้องการใช้ openX โดยไม่บันทึกดังนั้นฉันต้องติดกับคำหลัก ฉันคิดว่าการใช้คีย์เวิร์ด down เหมือนกันกับการ

@xMRW พวกเขาไม่เหมือนกันเพราะคุณสามารถส่งพจนานุกรมเป็นพารามิเตอร์ไปยังฟังก์ชั่นใดก็ได้เช่นกัน # 1 ของคุณถูกต้องแล้ว
Keith

8

การขยายคำตอบของ @gecco ต่อไปนี้เป็นตัวอย่างที่จะแสดงให้คุณเห็นถึงความแตกต่าง:

def foo(**kwargs):
    for entry in kwargs.items():
        print("Key: {}, value: {}".format(entry[0], entry[1]))

# call using normal keys:
foo(a=1, b=2, c=3)
# call using an unpacked dictionary:
foo(**{"a": 1, "b":2, "c":3})

# call using a dictionary fails because the function will think you are
# giving it a positional argument
foo({"a": 1, "b": 2, "c": 3})
# this yields the same error as any other positional argument
foo(3)
foo("string")

ที่นี่คุณสามารถดูว่าการเปิดพจนานุกรมออกได้อย่างไรและทำไมการส่งพจนานุกรมจริงจึงล้มเหลว


1

เพราะพจนานุกรมเป็นค่าเดียว คุณต้องใช้การขยายคำหลักหากคุณต้องการที่จะผ่านมันเป็นกลุ่มของการขัดแย้งคำหลัก


ขออภัย แต่ "การขยายคำหลัก" คืออะไร คุณหมายถึงฉันควรใช้ dict_var แทน ** args และใช้ def func (อาร์กิวเมนต์, dict_var = 0) ... func (1, {1: "a", 2: "b"})

1

สำหรับ args # 2 จะเป็นเพียงพารามิเตอร์ทางการที่มีค่า dict แต่ไม่ใช่พารามิเตอร์ประเภทคำหลัก

หากคุณต้องการส่งพารามิเตอร์ประเภทคำหลักไปยังอาร์กิวเมนต์คำหลักคุณต้องระบุ ** เฉพาะหน้าพจนานุกรมของคุณซึ่งหมายถึง ** เกิดขึ้น

ตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับการใช้ ** kw

http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/


** kwargs กับ dict แตกต่างกันมาก?

ขอบคุณฉันมักจะอ่านเพิ่มเติมเกี่ยวกับหัวข้อที่ฉันไม่เข้าใจ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.