ตัวดำเนินการดาวหมายถึงอะไรในการเรียกใช้ฟังก์ชัน


631

ตัว*ดำเนินการหมายถึงอะไรใน Python เช่นในโค้ด like zip(*x)หรือf(**k)?

  1. วิธีการจัดการภายในล่าม?
  2. มีผลต่อประสิทธิภาพหรือไม่? เร็วหรือช้า?
  3. มีประโยชน์เมื่อใดและเมื่อใดไม่เป็นประโยชน์?
  4. ควรใช้ในการประกาศฟังก์ชันหรือในการโทร?


4
ฉันคิดว่าสิ่งนี้ควรจะเรียกว่า "* function call syntax" พวกเขาจะไม่ประกอบแม้ว่ามันจะได้รับการทำให้เกิดความสับสนที่มีเป็น*และ**ผู้ประกอบการที่มีอะไรจะทำอย่างไรกับรูปแบบนี้
Ian Bicking

1
@Ian Bicking: คุณมีสิทธิเต็มที่ * และ ** ในรายการอาร์กิวเมนต์เป็นไวยากรณ์ที่บริสุทธิ์ (โทเค็น)
P. Ortiz

2
หมายเหตุ: สำหรับPEP 448:สิ่งที่เฉพาะเจาะจงสำหรับการคลายการบรรจุหีบห่อเพิ่มเติม (เช่น[*a, b, *c]หรือ{**d1, **d2}) คุณจะต้องอ่านเครื่องหมายดอกจันในทูเพิลแสดงรายการและกำหนดคำจำกัดความเครื่องหมายดอกจันคู่ในคำจำกัดความของคำสั่งซึ่งเฉพาะเจาะจงสำหรับการใช้งานนอกการเรียกฟังก์ชันและคำจำกัดความของฟังก์ชัน . สำหรับก่อนหน้านี้PEP 3132ดูการกำหนดเอาออกหลายในหลามเมื่อคุณไม่ทราบระยะเวลาในลำดับ
ShadowRanger

1
VTR - ไม่ซ้ำกับอะไร ** (ดาวคู่ / ดอกจัน) และ * (ดาว / ดอกจัน) ทำกับพารามิเตอร์ เนื่องจากคำถามนั้นเกี่ยวกับพารามิเตอร์เท่านั้นแม้ว่าคำตอบจะครอบคลุมการเรียกฟังก์ชันด้วยก็ตาม เครื่องหมายดอกจันในการเรียกใช้ฟังก์ชันควรทำเครื่องหมายว่าซ้ำกันของคำถามนี้เนื่องจากเป็นที่นิยมน้อยกว่าและคำตอบด้านบนจะไม่สมบูรณ์
wjandrea

คำตอบ:


968

ดาวดวงเดียว*คลายลำดับ / คอลเลกชันเป็นอาร์กิวเมนต์ตำแหน่งดังนั้นคุณสามารถทำได้:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

สิ่งนี้จะคลาย tuple เพื่อให้ดำเนินการเป็น:

s = sum(1, 2)

ดาวคู่**ทำเช่นเดียวกันโดยใช้พจนานุกรมเท่านั้นจึงตั้งชื่ออาร์กิวเมนต์:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

คุณยังสามารถรวม:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

จะดำเนินการเป็น:

s = sum(1, 2, c=10, d=15)

ดูส่วน4.7.4 - การคลายการบรรจุรายการอาร์กิวเมนต์ของเอกสาร Python


นอกจากนี้คุณสามารถกำหนดฟังก์ชันที่จะใช้*xและ**yอาร์กิวเมนต์ได้ซึ่งจะทำให้ฟังก์ชันยอมรับอาร์กิวเมนต์ตำแหน่งและ / หรือระบุชื่อจำนวนเท่าใดก็ได้ที่ไม่ได้ระบุชื่อเฉพาะในการประกาศ

ตัวอย่าง:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

หรือด้วย**:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

สิ่งนี้ช่วยให้คุณระบุพารามิเตอร์ทางเลือกจำนวนมากได้โดยไม่ต้องประกาศ

และอีกครั้งคุณสามารถรวม:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

4
ทำไมคุณถึงต้องการสิ่งนี้ฟังก์ชั่นนี้ไม่สามารถวนซ้ำในรายการที่ให้มาโดยไม่ต้องขยายได้?
Martin Beckett

30
แน่นอน แต่แล้วคุณจะต้องเรียกมันว่า: s = sum((1, 2, 3, 4, 5))หรือs = sum([1, 2, 3, 4, 5])ที่*valuesตัวเลือกทำให้มองโทรเหมือนว่ามันจะใช้เวลาจำนวนของการขัดแย้ง แต่พวกเขากำลังบรรจุลงในคอลเลกชันสำหรับรหัสฟังก์ชั่น
Lasse V.Karlsen

12
นี่คือประโยชน์ที่แท้จริง: คุณสามารถเขียนฟังก์ชันที่เป็นไปไม่ได้หากคุณจำเป็นต้องมีอาร์กิวเมนต์จำนวนตัวแปร ตัวอย่างเช่นฟังก์ชัน printf ของ C ซึ่งมีอาร์กิวเมนต์ 1 + n นั้นยากที่จะเขียนเป็นแบบฝึกหัดสำหรับโปรแกรมเมอร์มือใหม่ ใน Python ผู้เริ่มต้นสามารถเขียน def printf (string_template, * args) และดำเนินการต่อได้
IceArdor

1
จะเกิดอะไรขึ้นถ้าคุณ (บางทีโดยไม่ตั้งใจ: p) แกะพจนานุกรมที่มีเพียงหนึ่ง * แทนที่จะเป็นสอง ดูเหมือนจะทำอะไรบางอย่างดูเหมือนว่ามีทูเปิลออกมา แต่ก็ไม่ชัดเจนนักว่ามันคืออะไร (แก้ไข: โอเคฉันคิดว่าคำตอบคือมันเพิ่งแกะกุญแจออกค่าจะถูกทิ้ง)
Ben Farmer

1
ตัวอย่างสุดท้ายบอกเป็นนัยว่า * และ ** ไม่เพียง แต่ทำการแกะกล่องเท่านั้น แต่ยังรวมถึงการบรรจุด้วย! ดูหน้าดีเยี่ยมcodingame.com/playgrounds/500/…
HCC เมื่อ

46

จุดเล็ก ๆ อย่างหนึ่ง: สิ่งเหล่านี้ไม่ใช่ตัวดำเนินการ ตัวดำเนินการใช้ในนิพจน์เพื่อสร้างค่าใหม่จากค่าที่มีอยู่ (1 + 2 กลายเป็น 3 ตัวอย่างเช่น * และ ** ต่อไปนี้เป็นส่วนหนึ่งของไวยากรณ์ของการประกาศฟังก์ชันและการเรียกใช้


7
โปรดทราบว่าเอกสาร Python เรียก * ในบริบทนี้ว่าโอเปอเรเตอร์ ฉันยอมรับว่านั่นเป็นการทำให้เข้าใจผิด
Christophe

ขอบคุณ. ฉันกำลังมองหาการอธิบายที่ชัดเจนของสิ่งนี้ในเอกสารอ้างอิง python แต่ก็ยังไม่เห็น ดังนั้นกฎสำหรับการเรียกฟังก์ชันโดยพื้นฐานแล้วว่า "*" หรือ "**" ที่อยู่ตอนต้นของนิพจน์ภายในการเรียกใช้ฟังก์ชันทำให้เกิดการขยายประเภทนี้หรือไม่?
nealmcb

21

ฉันพบว่าสิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณต้องการ "จัดเก็บ" การเรียกใช้ฟังก์ชัน

ตัวอย่างเช่นสมมติว่าฉันมีการทดสอบหน่วยสำหรับฟังก์ชัน 'add':

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

ไม่มีวิธีอื่นใดในการเรียกเพิ่มนอกเหนือจากการทำสิ่งต่างๆด้วยตนเองเช่นเพิ่ม (ทดสอบ [0] ทดสอบ [1]) ซึ่งน่าเกลียด นอกจากนี้หากมีตัวแปรจำนวนตัวแปรโค้ดอาจดูน่าเกลียดพอสมควรกับ if-statement ทั้งหมดที่คุณต้องการ

สถานที่อื่นที่มีประโยชน์คือการกำหนดวัตถุจากโรงงาน (วัตถุที่สร้างวัตถุให้คุณ) สมมติว่าคุณมีคลาส Factory ที่สร้างวัตถุรถยนต์และส่งคืน คุณสามารถทำให้ myFactory.make_car ('red', 'bmw', '335ix') สร้าง Car ('red', 'bmw', '335ix') แล้วส่งกลับ

def make_car(*args):
   return Car(*args)

นอกจากนี้ยังมีประโยชน์เมื่อคุณต้องการเรียกคอนสตรัคเตอร์ของ superclass


4
ฉันชอบตัวอย่างของคุณ แต่ฉันคิดว่า -1 + 3 == 2.
eksortso

5
ฉันตั้งใจใส่บางอย่างลงไปที่นั่นซึ่งจะล้มเหลว :)
Donald Miner

19

เรียกว่าไวยากรณ์การโทรแบบขยาย จากเอกสารประกอบ :

หากนิพจน์ไวยากรณ์ * ปรากฏในการเรียกใช้ฟังก์ชันนิพจน์ต้องประเมินเป็นลำดับ องค์ประกอบจากลำดับนี้จะถือว่าเป็นอาร์กิวเมนต์ตำแหน่งเพิ่มเติม หากมีอาร์กิวเมนต์ตำแหน่ง x1, ... , xN และนิพจน์ที่ประเมินเป็นลำดับ y1, ... , yM สิ่งนี้จะเทียบเท่ากับการเรียกที่มีอาร์กิวเมนต์ตำแหน่ง M + N x1, ... , xN, y1, .. , yM.

และ:

หากนิพจน์ไวยากรณ์ ** ปรากฏในการเรียกใช้ฟังก์ชันนิพจน์ต้องประเมินเป็นการแม็ปซึ่งเนื้อหาจะถือว่าเป็นอาร์กิวเมนต์คีย์เวิร์ดเพิ่มเติม ในกรณีของคีย์เวิร์ดปรากฏในทั้งนิพจน์และเป็นอาร์กิวเมนต์คีย์เวิร์ดที่ชัดเจนจะมีการเพิ่มข้อยกเว้น TypeError


3
เพียงแค่เพิ่มเชิงอรรถในคำตอบของตำรา - ก่อนที่การสนับสนุนทางไวยากรณ์จะมาถึงฟังก์ชันเดียวกันนี้ก็สามารถทำได้ด้วยapply()ฟังก์ชันในตัว
Jeremy Brown

18

ในการเรียกใช้ฟังก์ชันดาวดวงเดียวจะเปิดรายชื่อเข้าสู่การขัดแย้งแยก (เช่นzip(*x)เป็นเช่นเดียวกับzip(x1,x2,x3)ถ้าx=[x1,x2,x3]) และดาวคู่จะเปิดพจนานุกรมเข้าไปในข้อโต้แย้งคำหลักที่แยกจากกัน (เช่นf(**k)เป็นเช่นเดียวกับถ้าf(x=my_x, y=my_y)k = {'x':my_x, 'y':my_y}

ในนิยามฟังก์ชันมันเป็นอีกทางหนึ่ง: ดาวดวงเดียวจะเปลี่ยนอาร์กิวเมนต์ตามจำนวนที่กำหนดให้เป็นรายการและการเริ่มต้นสองครั้งจะเปลี่ยนอาร์กิวเมนต์คำหลักตามจำนวนที่กำหนดลงในพจนานุกรม เช่นdef foo(*x)หมายถึง "foo ใช้เวลาจำนวนข้อของการขัดแย้งและพวกเขาจะสามารถเข้าถึงได้ผ่านรายการ x (เช่นถ้าโทรของผู้ใช้foo(1,2,3), xจะ[1,2,3])" และdef bar(**k)หมายถึง "บาร์ใช้เวลาจำนวนข้อของข้อโต้แย้งคำหลักและพวกเขาจะสามารถเข้าถึงได้ผ่าน k พจนานุกรม (เช่นถ้าโทรของผู้ใช้bar(x=42, y=23), kจะ{'x': 42, 'y': 23})"


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