ฉันขอแนะนำให้อ่านPEP 483และPEP 484และดูสิ่งนี้การนำเสนอโดย Guido เกี่ยวกับ Type Hinting
สรุป :ประเภทเค้าเป็นอักษรสิ่งที่คำหมายถึงคุณคำแนะนำชนิดของวัตถุ (s) คุณกำลังใช้
เนื่องจากแบบไดนามิกธรรมชาติของงูหลามอนุมานหรือการตรวจสอบชนิดของวัตถุที่ถูกนำมาใช้เป็นเรื่องยากโดยเฉพาะอย่างยิ่ง ความจริงเรื่องนี้ทำให้นักพัฒนาเข้าใจยากว่าเกิดอะไรขึ้นในโค้ดที่พวกเขาไม่ได้เขียนและที่สำคัญที่สุดคือสำหรับเครื่องมือตรวจสอบชนิดที่พบใน IDEs จำนวนมาก [PyCharm, PyDev มาถึงใจ] ที่ จำกัด เนื่องจากข้อเท็จจริงที่ว่า ไม่มีตัวบ่งชี้ประเภทของวัตถุใด ๆ เป็นผลให้พวกเขาพยายามที่จะสรุปประเภทด้วย (ดังกล่าวในการนำเสนอ) ประมาณ 50% อัตราความสำเร็จ
ในการรับสองสไลด์ที่สำคัญจากงานนำเสนอการพิมพ์คำแนะนำ:
ทำไมต้องพิมพ์คำใบ้?
- ช่วยให้ตัวตรวจสอบชนิด:โดยการบอกใบ้ว่าคุณต้องการให้วัตถุชนิดใดเป็นตัวตรวจสอบชนิดสามารถตรวจจับตัวอย่างเช่นถ้าคุณกำลังผ่านวัตถุที่มีประเภทที่ไม่คาดคิด
- จะช่วยให้มีเอกสาร:
TypeErrors
บุคคลที่สามดูรหัสของคุณจะได้รู้ว่าสิ่งที่คาดหวังที่เพราะฉะนั้นวิธีการใช้งานโดยไม่ได้รับพวกเขา
- ช่วยให้ IDEs พัฒนาเครื่องมือที่แม่นยำและมีประสิทธิภาพมากขึ้น:สภาพแวดล้อมการพัฒนาจะเหมาะสมกว่าในการแนะนำวิธีการที่เหมาะสมเมื่อทราบว่าวัตถุของคุณคือประเภทใด คุณอาจเคยเจอกับ IDE บางตัวในขณะนี้
.
และมีเมธอด / แอ็ตทริบิวต์ปรากฏขึ้นซึ่งไม่ได้กำหนดไว้สำหรับวัตถุ
ทำไมต้องใช้เครื่องตรวจสอบชนิดคงที่?
- ค้นหาข้อบกพร่องเร็วกว่านี้: นี่คือตัวเองชัดเจนฉันเชื่อ
- ยิ่งโครงการของคุณใหญ่ขึ้นเท่าไหร่คุณก็ยิ่งต้องการมันมากขึ้นเท่านั้น ภาษาแบบคงที่มีความแข็งแกร่งและการควบคุมที่ขาดภาษาแบบไดนามิก แอปพลิเคชันของคุณที่ใหญ่และซับซ้อนยิ่งขึ้นจะกลายเป็นตัวควบคุมและการคาดการณ์ได้มากขึ้น (จากลักษณะพฤติกรรม) ที่คุณต้องการ
- ทีมขนาดใหญ่กำลังทำการวิเคราะห์แบบคงที่อยู่แล้ว : ฉันเดาว่าสิ่งนี้จะตรวจสอบสองจุดแรก
ในฐานะที่เป็นบันทึกปิดสำหรับการแนะนำเล็ก ๆ นี้ : นี่เป็นตัวเลือกคุณสมบัติและจากสิ่งที่ฉันเข้าใจมันได้ถูกนำมาใช้เพื่อเก็บเกี่ยวผลประโยชน์บางส่วนของการพิมพ์แบบสแตติก
โดยทั่วไปคุณไม่จำเป็นต้องกังวลเกี่ยวกับเรื่องนี้และไม่จำเป็นต้องใช้อย่างแน่นอน (โดยเฉพาะในกรณีที่คุณใช้ Python เป็นภาษาสคริปต์เสริม) มันควรจะเป็นประโยชน์เมื่อมีการพัฒนาโครงการที่มีขนาดใหญ่เป็นมันมีความทนทานที่จำเป็นมากในการควบคุมและความสามารถในการแก้จุดบกพร่องเพิ่มเติม
พิมพ์คำใบ้ด้วย mypy :
เพื่อให้คำตอบนี้สมบูรณ์ยิ่งขึ้นฉันคิดว่าการสาธิตเล็ก ๆ น่าจะเหมาะสม ฉันจะใช้mypy
ห้องสมุดที่เป็นแรงบันดาลใจให้พิมพ์คำแนะนำตามที่ปรากฏใน PEP ส่วนใหญ่จะเขียนไว้สำหรับผู้ที่ชนกับคำถามนี้และสงสัยว่าจะเริ่มต้นอย่างไร
ก่อนที่ฉันจะทำอย่างนั้นให้ฉันย้ำสิ่งต่อไปนี้: PEP 484ไม่ได้บังคับอะไรเลย มันเป็นเพียงการกำหนดทิศทางสำหรับคำอธิบายประกอบฟังก์ชั่นและเสนอแนวทางสำหรับวิธีการตรวจสอบประเภทสามารถ / ควรจะดำเนินการ คุณสามารถใส่คำอธิบายประกอบฟังก์ชั่นและคำใบ้ได้หลายอย่างตามที่คุณต้องการ สคริปต์ของคุณจะยังคงทำงานไม่ว่าจะมีคำอธิบายประกอบหรือไม่เพราะ Python ไม่ได้ใช้งาน
อย่างไรก็ตามตามที่ระบุไว้ใน PEP ประเภทคำใบ้โดยทั่วไปควรมีสามรูปแบบ:
นอกจากนี้คุณจะต้องการที่จะใช้คำแนะนำประเภทร่วมกับใหม่โมดูลนำมาใช้ในtyping
Py3.5
ในนั้น ABCs (ชั้นฐานบทคัดย่อ) จำนวนมากถูกกำหนดพร้อมกับฟังก์ชั่นผู้ช่วยและผู้ตกแต่งเพื่อใช้ในการตรวจสอบแบบคงที่ ส่วนใหญ่ABCs
ในการcollections.abc
ที่จะถูกรวม แต่ในGeneric
รูปแบบเพื่อให้การสมัครสมาชิก (โดยกำหนด__getitem__()
วิธีการ)
สำหรับผู้ที่สนใจคำอธิบายในเชิงลึกมากขึ้นสิ่งเหล่านี้mypy documentation
เขียนได้ดีมากและมีตัวอย่างโค้ดจำนวนมากที่แสดง / อธิบายการทำงานของตัวตรวจสอบ มันคุ้มค่าที่จะอ่าน
คำอธิบายประกอบฟังก์ชั่นและความคิดเห็นพิเศษ:
ก่อนอื่นมันน่าสนใจที่จะสังเกตพฤติกรรมบางอย่างที่เราสามารถทำได้เมื่อใช้ความคิดเห็นพิเศษ # type: type
สามารถเพิ่มข้อคิดเห็นพิเศษระหว่างการกำหนดตัวแปรเพื่อระบุประเภทของวัตถุหากไม่สามารถอนุมานได้โดยตรง โดยทั่วไปการมอบหมายอย่างง่ายนั้นอนุมานได้ง่าย แต่คนอื่น ๆ เช่นรายการ (เกี่ยวกับเนื้อหาของพวกเขา) ไม่สามารถทำได้
หมายเหตุ:ถ้าเราต้องการใช้อนุพันธ์ของContainers
และจำเป็นต้องระบุเนื้อหาสำหรับคอนเทนเนอร์นั้นเราต้องใช้ประเภททั่วไปจากtyping
โมดูล รองรับการทำดัชนี
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
ถ้าเราเพิ่มคำสั่งเหล่านี้ไปยังแฟ้มและดำเนินการให้มีล่ามของเราทุกอย่างทำงานได้ดีและเพียงแค่พิมพ์เนื้อหาของรายการprint(a)
ความคิดเห็นที่ได้รับการทิ้งถือว่าเป็นความเห็นธรรมดาที่ไม่มีความหมายเพิ่มเติมa
# type
ถือว่าเป็นความเห็นธรรมดาที่ไม่มีความหมายเพิ่มเติม
โดยการทำงานนี้กับmypy
ในมืออื่น ๆ ที่เราได้รับการตอบสนองต่อไปนี้:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
การระบุว่ารายการของstr
วัตถุไม่สามารถมีสิ่งint
ที่พูดแบบคงที่คือเสียง สิ่งนี้สามารถแก้ไขได้โดยการปฏิบัติตามประเภทa
และต่อท้ายstr
วัตถุเท่านั้นหรือโดยการเปลี่ยนประเภทของเนื้อหาของa
เพื่อบ่งชี้ว่าค่าใด ๆ ที่เป็นที่ยอมรับ (ดำเนินการอย่างสังหรณ์ใจด้วยList[Any]
หลังจากAny
นำเข้าจากtyping
)
คำอธิบายประกอบฟังก์ชั่นจะถูกเพิ่มในรูปแบบparam_name : type
หลังจากแต่ละพารามิเตอร์ในฟังก์ชั่นลายเซ็นของคุณและประเภทกลับมามีการระบุโดยใช้-> type
สัญกรณ์ก่อนลำไส้ใหญ่ฟังก์ชั่นสิ้นสุด; คำอธิบายประกอบทั้งหมดจะถูกเก็บไว้ใน__annotations__
แอตทริบิวต์สำหรับฟังก์ชั่นนั้นในรูปแบบพจนานุกรมที่มีประโยชน์ ใช้ตัวอย่างเล็ก ๆ น้อย ๆ (ซึ่งไม่ต้องการประเภทพิเศษจากtyping
โมดูล):
def annotated(x: int, y: str) -> bool:
return x < y
annotated.__annotations__
แอตทริบิวต์ตอนนี้มีค่าต่อไปนี้:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
หากเราเป็น noobie ที่สมบูรณ์หรือเราคุ้นเคยกับPy2.7
แนวคิดและด้วยเหตุนี้จึงไม่รู้จักที่TypeError
ซ่อนตัวในการเปรียบเทียบannotated
เราสามารถดำเนินการตรวจสอบแบบคงที่อีกครั้งตรวจจับข้อผิดพลาดและช่วยให้เรามีปัญหาบางอย่าง:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
เหนือสิ่งอื่นใดการเรียกใช้ฟังก์ชันที่มีอาร์กิวเมนต์ที่ไม่ถูกต้องจะถูกดักจับด้วย:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
สิ่งเหล่านี้สามารถขยายไปยังพื้นการใช้งานใด ๆ และข้อผิดพลาดที่จับได้ขยายไปไกลกว่าการโทรและการดำเนินการขั้นพื้นฐาน ประเภทที่คุณสามารถตรวจสอบได้นั้นมีความยืดหยุ่นจริงๆและฉันเพิ่งจะได้เห็นจุดสูงสุดของศักยภาพ ดูในtyping
โมดูล PEPs หรือmypy
เอกสารที่จะทำให้คุณมีความคิดที่ครอบคลุมมากขึ้นของความสามารถที่นำเสนอ
ไฟล์ Stub:
สามารถใช้ไฟล์ Stub ได้ในสองกรณีที่แตกต่างกัน
- คุณต้องพิมพ์ตรวจสอบโมดูลที่คุณไม่ต้องการเปลี่ยนลายเซ็นฟังก์ชันโดยตรง
- คุณต้องการเขียนโมดูลและมีการตรวจสอบประเภท แต่ต้องการแยกหมายเหตุประกอบเพิ่มเติมจากเนื้อหา
ไฟล์ stub ใด (พร้อมส่วนขยาย.pyi
) คืออินเทอร์เฟซที่มีคำอธิบายประกอบของโมดูลที่คุณกำลังสร้าง / ต้องการใช้ พวกเขามีลายเซ็นของฟังก์ชั่นที่คุณต้องการที่จะพิมพ์ตรวจสอบกับร่างกายของฟังก์ชั่นทิ้ง รับความรู้สึกของสิ่งนี้โดยกำหนดฟังก์ชั่นสุ่มสามชุดในโมดูลชื่อrandfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
เราสามารถสร้างไฟล์ต้นขั้วrandfunc.pyi
ซึ่งเราสามารถวางข้อ จำกัด บางอย่างถ้าเราต้องการที่จะทำ ข้อเสียคือคนที่ดูแหล่งข้อมูลโดยไม่ต้องมีต้นขั้วจะไม่ได้รับความช่วยเหลือในการเพิ่มความคิดเห็นเมื่อพยายามทำความเข้าใจกับสิ่งที่ควรจะผ่าน
อย่างไรก็ตามโครงสร้างของไฟล์ stub นั้นค่อนข้างง่าย: เพิ่มคำจำกัดความของฟังก์ชั่นทั้งหมดโดยใช้วัตถุที่ว่างเปล่า ( pass
เต็มไป) และใส่คำอธิบายประกอบตามความต้องการของคุณ ที่นี่สมมติว่าเราต้องการทำงานกับint
ประเภทสำหรับตู้คอนเทนเนอร์ของเราเท่านั้น
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
combine
ฟังก์ชั่นให้ข้อบ่งชี้ว่าทำไมคุณอาจต้องการที่จะใช้คำอธิบายประกอบในแฟ้มที่แตกต่างกันที่พวกเขาบางครั้งถ่วงขึ้นรหัสและลดการอ่าน (ใหญ่ไม่มีไม่มีสำหรับงูใหญ่) แน่นอนคุณสามารถใช้นามแฝงประเภท แต่บางครั้งก็สับสนมากกว่าที่จะช่วยได้ (ใช้อย่างชาญฉลาด)
สิ่งนี้จะทำให้คุณคุ้นเคยกับแนวคิดพื้นฐานของ Type Hints ใน Python แม้ว่าตัวตรวจสอบชนิดที่ใช้นั้นเป็น
mypy
สิ่งที่คุณควรเริ่มเห็นผุดขึ้นมาบ้าง แต่ภายในบางตัวใน IDEs ( PyCharm ,) และอื่น ๆ เป็นโมดูลหลามมาตรฐาน ฉันจะลองและเพิ่มตัวตรวจสอบเพิ่มเติม / แพ็คเกจที่เกี่ยวข้องในรายการต่อไปนี้เมื่อใดและถ้าฉันพบพวกเขา (หรือถ้าแนะนำ)
ตัวตรวจสอบที่ฉันรู้จัก :
- Mypy : ตามที่อธิบายไว้ที่นี่
- PyType : โดย Google ใช้สัญลักษณ์ที่แตกต่างจากสิ่งที่ฉันรวบรวมอาจน่าดู
แพ็คเกจ / โครงการที่เกี่ยวข้อง :
- typeshed: repo Python อย่างเป็นทางการซึ่งมีไฟล์ stub หลากหลายประเภทสำหรับไลบรารี่มาตรฐาน
typeshed
โครงการเป็นจริงหนึ่งในสถานที่ที่ดีที่สุดที่คุณสามารถดูเพื่อดูว่าประเภทเค้าอาจจะใช้ในโครงการของคุณเอง ลองมาเป็นตัวอย่างdunders ของชั้นในที่สอดคล้องกันไฟล์:__init__
Counter
.pyi
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
ไหน_T = TypeVar('_T')
จะใช้ในการกำหนดชั้นเรียนทั่วไป สำหรับCounter
ชั้นเรียนเราจะเห็นว่ามันไม่สามารถรับอาร์กิวเมนต์ในเครื่องมือเริ่มต้นได้Mapping
จากประเภทใดประเภทหนึ่งไปยังint
หรือรับIterable
ประเภทใดก็ได้
แจ้งให้ทราบล่วงหน้า : สิ่งหนึ่งที่ฉันลืมที่จะกล่าวถึงคือการที่typing
โมดูลได้รับการแนะนำบนพื้นฐานชั่วคราว จากPEP 411 :
แพ็คเกจชั่วคราวอาจมีการแก้ไข API ก่อนที่จะ "เปลี่ยน" เป็นสถานะ "เสถียร" ในแง่หนึ่งสถานะนี้ให้ประโยชน์ของการเป็นส่วนหนึ่งของการแจกจ่าย Python อย่างเป็นทางการ ในทางกลับกันทีมพัฒนาหลักระบุอย่างชัดเจนว่าไม่มีสัญญาใด ๆ เกี่ยวกับความเสถียรของ API ของแพ็คเกจซึ่งอาจเปลี่ยนแปลงได้สำหรับการเปิดตัวครั้งถัดไป แม้ว่าจะถือว่าเป็นผลลัพธ์ที่ไม่น่าเป็นไปได้แพคเกจดังกล่าวอาจถูกลบออกจากไลบรารีมาตรฐานโดยไม่มีช่วงเวลาที่ไม่ยอมรับหากข้อกังวลเกี่ยวกับ API หรือการบำรุงรักษาของพวกเขาพิสูจน์แล้วว่าเป็นที่ยอมรับ
ดังนั้นหยิบของที่นี่ด้วยเกลือนิดหน่อย ฉันสงสัยว่ามันจะถูกลบออกหรือเปลี่ยนแปลงด้วยวิธีการที่สำคัญ แต่ไม่มีใครรู้
**หัวข้ออื่นทั้งหมด แต่ใช้ได้ในขอบเขตของคำใบ้ประเภทPEP 526
:: ไวยากรณ์สำหรับคำอธิบายประกอบตัวแปรเป็นความพยายามที่จะแทนที่# type
ความคิดเห็นโดยการแนะนำไวยากรณ์ใหม่ที่ช่วยให้ผู้ใช้คำอธิบายประกอบประเภทของตัวแปรในvarname: type
งบง่าย
ดูหมายเหตุประกอบแบบผันแปรใน Python 3.6 คืออะไร ดังที่ได้กล่าวไว้ก่อนหน้านี้สำหรับคำแนะนำเล็ก ๆ เกี่ยวกับสิ่งเหล่านี้