คำอธิบายประกอบของฟังก์ชันที่นำมาใช้ใน Python 3.0 จะเพิ่มคุณลักษณะที่ช่วยให้คุณสามารถเพิ่มข้อมูลเมตาตามอำเภอใจให้กับพารามิเตอร์ของฟังก์ชันและคืนค่าได้ ตั้งแต่ python 3 มีการเพิ่มคำอธิบายประกอบฟังก์ชันลงใน python (PEP-3107) อย่างเป็นทางการ จุดประสงค์หลักคือการมีวิธีมาตรฐานในการเชื่อมโยงข้อมูลเมตากับพารามิเตอร์ของฟังก์ชันและคืนค่า
พื้นฐานของคำอธิบายประกอบฟังก์ชัน
มาทำความเข้าใจพื้นฐานของคำอธิบายประกอบฟังก์ชันกันเถอะ -
-
คำอธิบายประกอบของฟังก์ชันเป็นทางเลือกที่สมบูรณ์ทั้งสำหรับพารามิเตอร์และค่าที่ส่งคืน
-
คำอธิบายประกอบของฟังก์ชันเป็นวิธีการเชื่อมโยงส่วนต่างๆ ของฟังก์ชันกับนิพจน์หลามโดยพลการ ณ เวลารวบรวม
-
PEP-3107 ไม่ได้พยายามแนะนำความหมายมาตรฐานใดๆ แม้แต่กับประเภทในตัว งานทั้งหมดนี้เหลือให้ห้องสมุดบุคคลที่สาม
ไวยากรณ์
คำอธิบายประกอบของพารามิเตอร์อย่างง่าย
คำอธิบายประกอบสำหรับพารามิเตอร์อยู่ในรูปแบบต่อไปนี้ -
def foo(x: expression, y: expression = 20): ….
ในขณะที่คำอธิบายประกอบสำหรับพารามิเตอร์ส่วนเกินจะเป็น −
def foo(**args: expression, **kwargs: expression): …..
ในกรณีของพารามิเตอร์ที่ซ้อนกัน คำอธิบายประกอบจะเป็นไปตามชื่อของพารามิเตอร์เสมอ และไม่อยู่จนกว่าจะถึงวงเล็บสุดท้าย ไม่จำเป็นต้องใส่คำอธิบายประกอบพารามิเตอร์ทั้งหมดของพารามิเตอร์ที่ซ้อนกัน
def foo(x1, y1: expression), (x2: expression, y2: expression)=(None, None)): ……
สิ่งสำคัญคือต้องเข้าใจว่า python ไม่ได้จัดเตรียมคำอธิบายประกอบเกี่ยวกับความหมายใดๆ มันให้การสนับสนุนทางวากยสัมพันธ์ที่ดีสำหรับการเชื่อมโยงข้อมูลเมตาเช่นเดียวกับวิธีง่ายๆ ในการเข้าถึง นอกจากนี้ยังไม่จำเป็นต้องใส่คำอธิบายประกอบ
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)
ในตัวอย่างข้างต้น ฟังก์ชัน func() รับพารามิเตอร์สามตัวที่เรียกว่า x,y และ z ในที่สุดก็พิมพ์ผลรวมของพวกมัน อาร์กิวเมนต์แรก x มีคำอธิบายประกอบด้วยสตริง 'ใส่คำอธิบายประกอบ x' อาร์กิวเมนต์ที่สอง y ถูกใส่คำอธิบายประกอบด้วยสตริง 'ใส่คำอธิบายประกอบ y' และอาร์กิวเมนต์ที่สาม z ถูกใส่คำอธิบายประกอบด้วยประเภท int ค่าที่ส่งคืนมีคำอธิบายประกอบด้วยประเภท float นี่คือไวยากรณ์ '->' สำหรับใส่คำอธิบายประกอบมูลค่าที่ส่งคืน
ผลลัพธ์
>>> func(2,3,-4) 1 >>> func('Function','-','Annotation') Function-Annotation
ข้างบนเราเรียก func() สองครั้ง ครั้งเดียวกับ int อาร์กิวเมนต์ และอีกครั้งกับ string อาร์กิวเมนต์ ในทั้งสองกรณี func() ทำสิ่งที่ถูกต้องและคำอธิบายประกอบจะถูกละเว้น ดังนั้น เราจึงเห็นว่าคำอธิบายประกอบไม่มีผลต่อการทำงานของฟังก์ชัน func()
การเข้าถึงคำอธิบายประกอบของฟังก์ชัน
คำอธิบายประกอบทั้งหมดจะถูกเก็บไว้ในพจนานุกรมที่เรียกว่า __annotations__ ซึ่งเป็นคุณลักษณะของฟังก์ชัน -
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z) >>> func.__annotations__ {'x': 'annotating x', 'y': 'annotating y', 'z': <class 'int'>, 'return': <class 'float'>}
ดังที่เราเห็นในตัวอย่างโค้ดก่อนหน้านี้ คำอธิบายประกอบไม่ได้ถูกพิมพ์ออกมา แม้ว่าจะสามารถนำมาใช้เพื่อจุดประสงค์นั้นได้อย่างแน่นอนและคล้ายกับรูปแบบการพิมพ์ที่ใช้ในภาษาอื่นๆ ดังที่แสดงด้านล่าง −
>>> def func(a: 'python', b: {'category: ' 'language'}) -> 'yep': pass >>> func.__annotations__ {'a': 'python', 'b': {'category: language'}, 'return': 'yep'} >>>
เป็นนิพจน์ที่กำหนดเอง ซึ่งหมายความว่าค่าที่กำหนดเองสามารถเก็บไว้ในพจนานุกรม __annotations__ แม้ว่าจะไม่ได้เพิ่มความสำคัญให้กับตัวไพ ธ อนมากนัก ยกเว้นว่าควรเก็บค่าไว้ กล่าวคือ การกำหนดพารามิเตอร์และประเภทการส่งคืนเป็นการใช้งานทั่วไปของคำอธิบายประกอบของฟังก์ชัน
มัณฑนากร @no_type_check
หากคุณพบว่าตัวเองใช้เครื่องมือที่ถือว่าคำอธิบายประกอบเป็นการประกาศประเภท แต่คุณต้องการใช้เพื่อวัตถุประสงค์อื่น ให้ใช้ @no_type_check มัณฑนากรมาตรฐานเพื่อยกเว้นฟังก์ชันของคุณจากการประมวลผลดังที่แสดงไว้ที่นี่ -
>>> from typing import no_type_check >>> @no_type_check def func(a: 'python', b: {'category: ' 'language'}) -> 'yep': pass >>>
โดยปกติ ไม่จำเป็นต้องทำเช่นนี้ เนื่องจากเครื่องมือส่วนใหญ่ที่ใช้คำอธิบายประกอบมีวิธีระบุคำอธิบายประกอบสำหรับคำอธิบายประกอบ มัณฑนากรมีไว้สำหรับปกป้องเคสมุมที่สิ่งต่าง ๆ คลุมเครือ
คำอธิบายประกอบเป็นอินพุตสำหรับผู้ตกแต่งฟังก์ชัน
คำอธิบายประกอบเข้ากันได้ดีกับนักตกแต่งเพราะค่าคำอธิบายประกอบเป็นวิธีที่ดีในการป้อนข้อมูลให้กับนักตกแต่ง และเครื่องห่อที่สร้างโดยนักตกแต่งก็เป็นสถานที่ที่ดีในการใส่โค้ดที่ให้ความหมายกับคำอธิบายประกอบ
from functools import wraps def adapted(func): @wraps(func) def wrapper(**kwargs): final_args = {} for name, value in kwargs. items(): adapt = func.__annotations__.get(name) if adapt is not None: final_args[name] = adapt(value) else: final_args[name] = value result = func(**final_args) adapt = func.__annotations__.get('result') if adapt is not None: return adapt(result) return result return wrapper @adapted def func(a: int, b: repr) -> str: return a
ดังนั้นมัณฑนากรที่ดัดแปลงจึงรวมฟังก์ชั่นไว้ในเสื้อคลุม Wrapper นี้ยอมรับเฉพาะอาร์กิวเมนต์ของคีย์เวิร์ด ซึ่งหมายความว่าแม้ว่าฟังก์ชันดั้งเดิมจะยอมรับอาร์กิวเมนต์ตำแหน่งได้ ก็ต้องระบุชื่อด้วย
เมื่อฟังก์ชันถูกห่อแล้ว wrapper จะค้นหาอะแดปเตอร์ในคำอธิบายประกอบพารามิเตอร์ของฟังก์ชันและนำไปใช้ก่อนที่จะส่งอาร์กิวเมนต์ไปยังฟังก์ชันจริง
เมื่อฟังก์ชันส่งคืน wrapper จะตรวจสอบอะแด็ปเตอร์ค่าตอบแทน หากพบจะนำไปใช้กับค่าที่ส่งคืนก่อนที่จะส่งคืนในที่สุด
เมื่อเราพิจารณาความหมายของสิ่งที่เกิดขึ้นที่นี่ มันค่อนข้างน่าประทับใจ เราได้แก้ไขความหมายของการส่งพารามิเตอร์ไปยังฟังก์ชันหรือคืนค่าหมายความว่าอย่างไร
อาร์กิวเมนต์ของคีย์เวิร์ด
บางครั้ง พารามิเตอร์ของเมธอดตั้งแต่หนึ่งตัวขึ้นไปไม่จำเป็นต้องมีการประมวลผลใด ๆ ยกเว้นการกำหนดให้กับแอตทริบิวต์ของตนเอง เราสามารถใช้มัณฑนากรและคำอธิบายประกอบเพื่อทำให้สิ่งนี้เกิดขึ้นโดยอัตโนมัติได้ไหม แน่นอนเราทำได้
from functools import wraps def store_args(func): @wraps(func) def wrapper(self, **kwargs): for name, value in kwargs.items(): attrib = func.__annotations__.get(name) if attrib is True: attrib = name if isinstance(attrib, str): setattr(self, attrib, value) return func(self, **kwargs) return wrapper class A: @store_args def __init__(self, first: True, second: 'example'): pass a = A(first = 5, second = 6) assert a.first == 5 assert a.example == 6