Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

คำอธิบายประกอบฟังก์ชันใน Python


คำอธิบายประกอบของฟังก์ชันที่นำมาใช้ใน 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