ความแตกต่างระหว่างการกำหนดการพิมพ์ Dict และ Dict?


106

ฉันกำลังฝึกใช้คำแนะนำประเภทใน Python 3.5 เพื่อนร่วมงานคนหนึ่งของฉันใช้typing.Dict:

import typing


def change_bandwidths(new_bandwidths: typing.Dict,
                      user_id: int,
                      user_name: str) -> bool:
    print(new_bandwidths, user_id, user_name)
    return False


def my_change_bandwidths(new_bandwidths: dict,
                         user_id: int,
                         user_name: str) ->bool:
    print(new_bandwidths, user_id, user_name)
    return True


def main():
    my_id, my_name = 23, "Tiras"
    simple_dict = {"Hello": "Moon"}
    change_bandwidths(simple_dict, my_id, my_name)
    new_dict = {"new": "energy source"}
    my_change_bandwidths(new_dict, my_id, my_name)

if __name__ == "__main__":
    main()

ทั้งสองทำงานได้ดีดูเหมือนจะไม่มีความแตกต่างกัน

ฉันได้อ่านไฟล์ typingเอกสารประกอบโมดูลแล้ว

ระหว่างtyping.Dictหรือdictตัวไหนที่ควรใช้ในโปรแกรม?


11
โปรดทราบว่า Python ไม่ได้บังคับใช้คำแนะนำประเภทจริงๆ เป็นเพียงคำใบ้ไม่ได้ใช้ในรันไทม์หรือแม้แต่เวลาคอมไพล์เพื่อบังคับใช้ประเภท Python อาจถูกพิมพ์อย่างรุนแรง (ตรงข้ามกับการพิมพ์ที่อ่อนแอ) และยังพิมพ์แบบไดนามิก (ตรงข้ามกับการพิมพ์แบบเข้มงวด) ดูPython ถูกพิมพ์อย่างแรงหรือไม่? . เครื่องมือภายนอกเช่น mypy สามารถใช้คำแนะนำเหล่านี้เพื่อช่วยให้คุณเขียนโค้ดได้ดีขึ้นอย่างไรก็ตามในกระบวนการที่เรียกว่าการวิเคราะห์แบบคงที่
Martijn Pieters

1
@MartijnPieters ฉันเคยชอบใช้คำแนะนำประเภทในรหัสของฉันควบคู่ไปกับ MyPy และแกล้งทำเป็นว่าฉันสามารถใช้ Python ด้วยความปลอดภัยประเภท น่าเสียดายที่มีรหัส A) ที่ใช้ไม่ได้กับ <3.4 และ B) ผู้คนที่หัวเราะเยาะฉันเพราะเห็นได้ชัดว่าคำใบ้ประเภทเป็นเรื่องที่น่าหัวเราะ มันค่อนข้างโชคร้ายจริงๆ
แมว

3
@cat: คำใบ้ประเภทได้รับการแนะนำให้รู้จักกับ Python โดยพนักงานของ Facebook เนื่องจากเราประสบความสำเร็จอย่างมากในการเพิ่มคุณสมบัติเดียวกันใน PHP (ดูแฮ็ค ) ใคร ๆ ก็หัวเราะไม่เคยสร้างโปรเจ็กต์ใหญ่ ๆ ที่มีวิศวกรมากกว่าหยิบมือเดียว
Martijn Pieters

3
@MartijnPieters ไม่ใช่def a(b: int) -> bool:เป็นข้อผิดพลาดทางไวยากรณ์ใน Python 2.7 และฉันคิดว่ามันเป็นข้อผิดพลาดทางไวยากรณ์ใน Python 3 เวอร์ชันเก่าด้วย
แมว

1
@cat: คุณกำลังพูดถึงคำอธิบายประกอบฟังก์ชันที่นี่ไวยากรณ์ที่ถูกเพิ่มใน Python 3.0 ดังนั้นเวอร์ชันเดียวที่เป็นข้อผิดพลาดทางไวยากรณ์คือ 2.7 ซึ่งเป็นสาเหตุที่ mypy สนับสนุนการใส่ข้อมูลนั้นในความคิดเห็น
Martijn Pieters

คำตอบ:


148

ไม่มีความแตกต่างอย่างแท้จริงระหว่างการใช้แบบธรรมดาtyping.Dictและdictไม่มี

อย่างไรก็ตามtyping.Dictเป็นประเภท Generic *ที่ให้คุณระบุประเภทของคีย์และค่าต่างๆเช่นกันทำให้มีความยืดหยุ่นมากขึ้น:

def change_bandwidths(new_bandwidths: typing.Dict[str, str],
                      user_id: int,
                      user_name: str) -> bool:

ด้วยเหตุนี้อาจเป็นไปได้ว่าในช่วงหนึ่งของอายุโครงการของคุณคุณต้องการกำหนดอาร์กิวเมนต์พจนานุกรมให้แม่นยำขึ้นเล็กน้อยเมื่อถึงจุดที่ขยาย typing.Dictไปtyping.Dict[key_type, value_type]เป็นการเปลี่ยนแปลงที่ 'เล็กลง' กว่าการแทนที่dictเปลี่ยนแทนที่

คุณสามารถทำให้สิ่งนี้กว้างขึ้นได้โดยใช้MappingหรือMutableMappingพิมพ์ที่นี่ เนื่องจากฟังก์ชั่นของคุณไม่จำเป็นต้องเปลี่ยนการทำแผนที่ฉันจึงยึดติดกับMappingการทำแผนที่ผมติดกับA dictคือการแมปเดียว แต่คุณสามารถสร้างวัตถุอื่น ๆ ที่ตอบสนองอินเทอร์เฟซการแมปได้เช่นกันและฟังก์ชันของคุณอาจยังใช้งานได้ดีกับสิ่งเหล่านี้:

def change_bandwidths(new_bandwidths: typing.Mapping[str, str],
                      user_id: int,
                      user_name: str) -> bool:

ตอนนี้คุณกำลังบอกผู้ใช้คนอื่นอย่างชัดเจนเกี่ยวกับฟังก์ชันนี้ว่ารหัสของคุณจะไม่เกิดขึ้นจริง เปลี่ยนแปลงการnew_bandwidthsแมปที่ส่งผ่าน

การใช้งานจริงของคุณเป็นเพียงการคาดหวังวัตถุที่สามารถพิมพ์ได้ นั่นอาจเป็นการทดสอบการใช้งาน แต่เนื่องจากโค้ดของคุณจะยังคงทำงานต่อไปหากคุณใช้new_bandwidths: typing.Anyเนื่องจากอ็อบเจ็กต์ใด ๆ ใน Python สามารถพิมพ์ได้


* : หมายเหตุ: หากคุณใช้ Python 3.7 หรือใหม่กว่าคุณสามารถใช้dictเป็นประเภททั่วไปได้หากคุณเริ่มต้นโมดูลด้วยfrom __future__ import annotationsPython 3.9 dict(รวมถึงคอนเทนเนอร์มาตรฐานอื่น ๆ ) รองรับการใช้เป็นประเภททั่วไปแม้ว่าจะไม่มีก็ตาม คำสั่ง


2
ตัวอย่างเพิ่มเติมที่เป็นประโยชน์คือเมื่อค่าพจนานุกรมสามารถเป็นประเภทต่างๆเช่น{"name": "bob", "age" : 51}จะเป็นเช่นนั้นtyping.Mapping[Union[str, int]หรือไม่? แล้วพจนานุกรมที่ซ้อนกัน{"person": {"name":"bob", "age": 51}จะเป็นtyping.Mapping[str, typing.Mapping[Union[str, int]]อย่างไร? การใช้Unionเช่นนั้นทำให้ฉันลำบากเพราะไม่ใช่สคีมาที่เข้มงวดเนื่องจากไม่มีการสั่งซื้อ อาจจะไม่เป็นไรหรือมีทางเลือกอื่น?
ดาวอส

1
ไม่เป็นไรเกี่ยวกับUnionคำถามที่ฉันเห็นว่ายังคงเป็นการสนทนาแบบเปิดอยู่github.com/python/typing/issues/28
ดาวอส

1
สิ่งนี้น่าสนใจมีประโยชน์และเกี่ยวข้องมากpython.org/dev/peps/pep-0589
Greg Hilston

@GregHilston: ที่จริงแล้วเกี่ยวกับวิธี จำกัด คีย์ที่พจนานุกรมสามารถมีได้และระบุประเภทที่เกี่ยวข้องแต่ละค่าที่ควรมี
Martijn Pieters

1
@GregHilston: อาใช่มันเป็น
Martijn Pieters

26

typing.Dictเป็นเวอร์ชันทั่วไปของdict:

class typing.Dict(dict, MutableMapping[KT, VT])

เวอร์ชันทั่วไปของ dict การใช้งานประเภทนี้มีดังนี้:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
     return word_list[word]

คุณสามารถระบุประเภทของคีย์และค่าใน dict ได้ที่นี่: Dict[str, int]

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