TL; DR : ถ้าคุณใช้ Python 4.0 มันใช้งานได้ ณ วันนี้ (2019) ใน 3.7+ คุณต้องเปิดใช้คุณสมบัตินี้โดยใช้คำสั่งในอนาคต ( from __future__ import annotations
) - สำหรับ Python 3.6 หรือต่ำกว่าใช้สตริง
ฉันเดาว่าคุณได้รับข้อยกเว้นนี้:
NameError: name 'Position' is not defined
นี่เป็นเพราะPosition
ต้องถูกกำหนดไว้ก่อนที่คุณจะสามารถใช้มันในการเพิ่มความคิดเห็นเว้นแต่ว่าคุณกำลังใช้ Python 4
Python 3.7+: from __future__ import annotations
งูหลาม 3.7 แนะนำPEP 563: การประเมินผลการเลื่อนออกไปคำอธิบายประกอบ โมดูลที่ใช้คำสั่งในอนาคตfrom __future__ import annotations
จะเก็บบันทึกย่อเป็นสตริงโดยอัตโนมัติ:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
สิ่งนี้ถูกกำหนดให้เป็นค่าเริ่มต้นใน Python 4.0 เนื่องจาก Python ยังคงเป็นภาษาที่พิมพ์แบบไดนามิกดังนั้นจึงไม่มีการตรวจสอบชนิดที่รันไทม์การพิมพ์คำอธิบายประกอบจึงไม่มีผลกระทบต่อประสิทธิภาพใช่ไหม ไม่ถูกต้อง! ก่อนที่ไพ ธ อน 3.7 โมดูลการพิมพ์เคยเป็นหนึ่งในโมดูลหลามที่ช้าที่สุดในแกนดังนั้นถ้าimport typing
คุณจะเห็นประสิทธิภาพเพิ่มขึ้นถึง 7 เท่าเมื่อคุณอัพเกรดเป็น 3.7
Python <3.7: ใช้สตริง
ตาม PEP 484คุณควรใช้สตริงแทนคลาส:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
หากคุณใช้กรอบ Django นี้อาจจะคุ้นเคยเป็นแบบจำลอง Django ยังใช้สตริงอ้างอิงข้างหน้า (คำจำกัดความที่สำคัญต่างประเทศที่รูปแบบต่างประเทศself
หรือไม่ได้ประกาศยัง) สิ่งนี้ควรทำงานกับ Pycharm และเครื่องมืออื่น ๆ
แหล่งที่มา
ชิ้นส่วนที่เกี่ยวข้องของPEP 484และPEP 563เพื่อสำรองการเดินทางของคุณ:
ส่งต่อการอ้างอิง
เมื่อคำใบ้ประเภทมีชื่อที่ยังไม่ได้กำหนดนิยามอาจจะแสดงเป็นตัวอักษรสตริงที่จะได้รับการแก้ไขในภายหลัง
สถานการณ์ที่สิ่งนี้เกิดขึ้นโดยทั่วไปคือคำจำกัดความของคลาสคอนเทนเนอร์ซึ่งคลาสที่ถูกนิยามนั้นเกิดขึ้นในลายเซ็นต์ของบางเมธอด ตัวอย่างเช่นรหัสต่อไปนี้ (การเริ่มต้นของการนำต้นไม้ไบนารีแบบง่าย) ไม่ทำงาน:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
เพื่อเขียนสิ่งนี้เราเขียน:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
สตริงตัวอักษรควรประกอบด้วยนิพจน์ Python ที่ถูกต้อง (เช่นคอมไพล์ (lit, '', 'eval') ควรเป็นอ็อบเจกต์โค้ดที่ถูกต้อง) และควรประเมินโดยไม่มีข้อผิดพลาดเมื่อโมดูลโหลดเต็มแล้ว เนมสเปซท้องถิ่นและทั่วโลกที่ได้รับการประเมินควรเป็นเนมสเปซเดียวกันซึ่งจะมีการประเมินอาร์กิวเมนต์เริ่มต้นสำหรับฟังก์ชันเดียวกัน
และ PEP 563:
ใน Python 4.0 คำอธิบายประกอบของฟังก์ชันและตัวแปรจะไม่ได้รับการประเมินอีกต่อไปตามเวลาที่กำหนด รูปแบบสตริงจะถูกเก็บไว้ใน__annotations__
พจนานุกรมที่เกี่ยวข้องแทน ตัวตรวจสอบชนิดคงที่จะไม่เห็นความแตกต่างในพฤติกรรมในขณะที่เครื่องมือที่ใช้คำอธิบายประกอบที่รันไทม์จะต้องทำการประเมินผลที่เลื่อนออกไป
...
ฟังก์ชั่นที่อธิบายไว้ข้างต้นสามารถเปิดใช้งานได้ตั้งแต่ Python 3.7 โดยใช้การนำเข้าพิเศษต่อไปนี้:
from __future__ import annotations
สิ่งที่คุณอาจถูกล่อลวงให้ทำแทน
A. กำหนดหุ่นจำลอง Position
ก่อนการกำหนดคลาสให้วางนิยามดัมมี:
class Position(object):
pass
class Position(object):
...
สิ่งนี้จะกำจัดNameError
และยังอาจดูโอเค:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
แต่มันคืออะไร
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Monkey-patch เพื่อเพิ่มหมายเหตุประกอบ:
คุณอาจต้องการลองใช้เวทมนตร์การเขียนโปรแกรมเมตางูหลามและเขียนมัณฑนากรเพื่อปรับแต่งคำจำกัดความของคลาสเพื่อเพิ่มคำอธิบายประกอบ:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
มัณฑนากรควรรับผิดชอบสิ่งนี้:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
อย่างน้อยก็ดูเหมือนว่าถูก:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
อาจเป็นปัญหามากเกินไป
ข้อสรุป
หากคุณใช้ 3.6 หรือต่ำกว่าให้ใช้สตริงตัวอักษรที่มีชื่อคลาสในการใช้ 3.7 from __future__ import annotations
และมันจะใช้งานได้