การเพิ่มค่าพารามิเตอร์เริ่มต้นด้วยคำใบ้ประเภทใน Python


236

ถ้าฉันมีฟังก์ชั่นเช่นนี้:

def foo(name, opts={}):
  pass

และฉันต้องการเพิ่มคำแนะนำประเภทให้กับพารามิเตอร์ฉันจะทำอย่างไร วิธีที่ฉันคิดว่าจะทำให้ฉันมีข้อผิดพลาดทางไวยากรณ์:

def foo(name: str, opts={}: dict) -> str:
  pass

ต่อไปนี้ไม่ได้เกิดข้อผิดพลาดทางไวยากรณ์ แต่ดูเหมือนจะเป็นวิธีที่ใช้งานง่ายในการจัดการกรณีนี้:

def foo(name: str, opts: dict={}) -> str:
  pass

ฉันไม่พบสิ่งใดในtypingเอกสารหรือในการค้นหาของ Google

แก้ไข: ฉันไม่ทราบว่าอาร์กิวเมนต์เริ่มต้นทำงานอย่างไรใน Python แต่เพื่อคำถามนี้ฉันจะเก็บตัวอย่างข้างต้น โดยทั่วไปการทำสิ่งต่อไปนี้ดีกว่ามาก:

def foo(name: str, opts: dict=None) -> str:
  if not opts:
    opts={}
  pass

4
ฟังก์ชั่นสุดท้ายเป็นวิธีที่ถูกต้อง มันเป็นวิธีเดียวกันกับที่scalaภาษาทำเช่นกัน
อิสราเอล Unterman

14
คุณมีประเภทเริ่มต้นที่ไม่แน่นอน - ซึ่งจะนำไปสู่ปัญหา
noɥʇʎԀʎzɐɹƆ

ดูคำตอบอัปเดตของฉัน @josh
noɥʇʎԀʎzɐɹƆ

@ noɥʇʎԀʎzɐɹƆไม่ยกเว้นว่าคุณจะใช้เพื่อเช่นบันทึกช่วยจำ : P
Mateen Ulhaq

คำตอบ:


281

วิธีที่สองของคุณถูกต้อง

def foo(opts: dict = {}):
    pass

print(foo.__annotations__)

ผลลัพธ์นี้

{'opts': <class 'dict'>}

เป็นความจริงที่ไม่ได้อยู่ในรายการPEP 484แต่คำใบ้ประเภทเป็นแอปพลิเคชันของคำอธิบายประกอบฟังก์ชั่นซึ่งมีการบันทึกไว้ใน PEP 3107 ส่วนไวยากรณ์ทำให้เห็นได้ชัดเจนว่าอาร์กิวเมนต์ของคำหลักทำงานกับคำอธิบายประกอบฟังก์ชัน

ฉันไม่แนะนำให้ใช้การโต้แย้งคำหลักที่ไม่แน่นอน ข้อมูลเพิ่มเติมที่นี่


1
ดูlegacy.python.org/dev/peps/pep-3107/#syntax การบอกประเภทเป็นเพียงแอปพลิเคชันของคำอธิบายประกอบฟังก์ชั่น
chepner

@chepner จริง ไม่ทราบว่า PEP 3107 มีบางอย่างเกี่ยวกับอาร์กิวเมนต์ของคำหลัก
noɥʇʎԀʎzɐɹƆ

2
ว้าวฉันไม่รู้เกี่ยวกับอาร์กิวเมนต์เริ่มต้นที่ไม่แน่นอนใน Python ... โดยเฉพาะอย่างยิ่งมาจาก Javascript / Ruby ซึ่งอาร์กิวเมนต์เริ่มต้นทำงานแตกต่างกัน จะไม่ทำอะไรซักอย่างที่ได้พูดไปแล้วว่ามีอาการคลื่นไส้เกี่ยวกับเรื่องนี้ฉันแค่ดีใจที่ฉันรู้เรื่องนี้มาก่อน ขอบคุณ!
จอช

6
ฉันได้รับคำแนะนำให้ใช้ไม่มีแทนที่จะเป็นประเภทที่ไม่แน่นอนเช่น {} หรือ [] หรือวัตถุเริ่มต้นเป็นการกลายพันธุ์ไปยังวัตถุนั้นโดยไม่ต้องคัดลอกลึกจะยังคงอยู่ระหว่างการทำซ้ำ
MrMesees

2
กำหนดฟังก์ชั่นที่เพียงพอพร้อมกับข้อโต้แย้งคำหลักที่ไม่แน่นอนและมันเป็นเพียงเรื่องของเวลาก่อนที่คุณจะพบว่าตัวเองมองย้อนกลับไปในเซสชั่นการดีบัก 4 ชั่วโมงเพื่อถามทางเลือกในชีวิตของคุณ
Joseph Sheedy

20

หากคุณกำลังใช้การพิมพ์ (แนะนำใน Python 3.5) คุณสามารถใช้typing.Optionalที่เทียบเท่ากับOptional[X] Union[X, None]มันถูกใช้เพื่อส่งสัญญาณว่าค่าที่ชัดเจนของNoneได้รับอนุญาต จากการพิมพ์ตัวเลือก :

def foo(arg: Optional[int] = None) -> None:
    ...

2
ไม่ควรที่จะไม่มีช่องว่างรอบ ๆ=ในOptional[int] = Noneเหมือนที่มันเป็นข้อตกลงสำหรับการขัดแย้งคำหลักที่ไม่ใช่คำใบ้ชนิด?
actual_panda

7

ฉันเพิ่งเห็นซับนี้:

def foo(name: str, opts: dict=None) -> str:
    opts = {} if not opts else opts
    pass

สวัสดี @Kirkalicious ขอบคุณสำหรับคำตอบของคุณ คุณอธิบายได้ไหมว่ามันทำงานอย่างไร
นาธาน

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