Computer >> Máy Tính >  >> Lập trình >> Python

Chú thích hàm trong Python

Chú thích hàm được giới thiệu trong Python 3.0 bổ sung một tính năng cho phép bạn thêm siêu dữ liệu tùy ý vào các tham số hàm và giá trị trả về. Kể từ python 3, chú thích hàm đã chính thức được thêm vào python (PEP-3107). Mục đích chính là có một cách chuẩn để liên kết siêu dữ liệu với các tham số hàm và trả về giá trị.

Khái niệm cơ bản về chú thích hàm

Hãy hiểu một số điều cơ bản về chú thích hàm -

  • Chú thích hàm hoàn toàn là tùy chọn cho cả tham số và giá trị trả về.

  • Chú thích hàm cung cấp cách liên kết các phần khác nhau của hàm với các biểu thức python tùy ý tại thời điểm biên dịch.

  • PEP-3107 không cố gắng giới thiệu bất kỳ loại ngữ nghĩa tiêu chuẩn nào, ngay cả đối với các loại tích hợp sẵn. Tất cả công việc này được để lại cho các thư viện của bên thứ ba.

Cú pháp

Chú thích của các tham số đơn giản

Chú thích cho các tham số có dạng sau -

def foo(x: expression, y: expression = 20):
   ….

Trong khi các chú thích cho các thông số dư thừa là -

def foo(**args: expression, **kwargs: expression):
   …..

Trong trường hợp các tham số lồng nhau, các chú thích luôn theo sau tên của các tham số và không cho đến dấu ngoặc đơn cuối cùng. Không bắt buộc phải chú thích tất cả các tham số của một tham số lồng nhau.

def foo(x1, y1: expression), (x2: expression, y2: expression)=(None, None)):
   ……

Điều quan trọng là phải hiểu rằng python không cung cấp bất kỳ ngữ nghĩa nào với chú thích. Nó chỉ cung cấp hỗ trợ cú pháp tốt để liên kết siêu dữ liệu cũng như một cách dễ dàng để truy cập nó. Ngoài ra, chú thích không phải là bắt buộc.

>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)

Trong ví dụ trên, hàm func () nhận ba tham số là x, y và z, cuối cùng in ra tổng của chúng. Đối số đầu tiên x được chú thích bằng chuỗi ‘chú thích x’, đối số thứ hai y được chú thích với chuỗi ‘chú thích y’ và đối số thứ ba z được chú thích với kiểu int. Giá trị trả về được chú thích bằng kiểu float. Đây là cú pháp ‘->’ để chú thích giá trị trả về.

Đầu ra

>>> func(2,3,-4)
1
>>> func('Function','-','Annotation')
Function-Annotation

Ở trên, chúng ta gọi hàm func () hai lần, một lần với đối số int và một lần với đối số chuỗi. Trong cả hai trường hợp, func () thực hiện đúng và các chú thích chỉ đơn giản là bị bỏ qua. Vì vậy, chúng tôi thấy các chú thích không ảnh hưởng đến việc thực thi hàm func ().

Truy cập chú thích chức năng

Tất cả các chú thích được lưu trữ trong một từ điển có tên __annotations__, bản thân nó là một thuộc tính của hàm -

>>> 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'>}

Như chúng ta có thể thấy trong ví dụ mã trước, chú thích không phải là khai báo được nhập, mặc dù chúng chắc chắn có thể được sử dụng cho mục đích đó và chúng giống với cú pháp nhập được sử dụng trong một số ngôn ngữ khác, như được hiển thị bên dưới -

>>> def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
   pass
>>> func.__annotations__
{'a': 'python', 'b': {'category: language'}, 'return': 'yep'}
>>>

Chúng là các biểu thức tùy ý, có nghĩa là các giá trị tùy ý có thể được lưu trữ trong từ điển __annotations__. Mặc dù vậy, chúng không bổ sung nhiều ý nghĩa cho bản thân python, ngoại trừ việc nó phải lưu trữ các giá trị. Điều đó nói rằng, việc xác định các kiểu tham số và trả về là cách sử dụng phổ biến của các chú thích hàm.

Trình trang trí @no_type_check

Nếu bạn thấy mình đang sử dụng một công cụ giả định chú thích là khai báo kiểu nhưng bạn muốn sử dụng chúng cho một số mục đích khác, hãy sử dụng trình trang trí @no_type_check tiêu chuẩn để loại bỏ chức năng của bạn khỏi quá trình xử lý như vậy, như được hiển thị ở đây -

>>> from typing import no_type_check
>>> @no_type_check
def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
   pass
>>>

Thông thường, điều này không bắt buộc vì hầu hết các công cụ sử dụng chú thích đều có cách nhận dạng những công cụ dành cho chúng. Trình trang trí là để bảo vệ các trường hợp góc, nơi mọi thứ không rõ ràng.

Chú thích làm đầu vào cho trình trang trí hàm

Chú thích kết hợp tốt với trình trang trí vì giá trị chú thích là một cách tốt để cung cấp thông tin đầu vào cho trình trang trí và trình bao bọc do trình trang trí tạo là một nơi tốt để đặt mã mang lại ý nghĩa cho chú thích.

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

Vì vậy, trình trang trí đã điều chỉnh bao bọc hàm trong một trình bao bọc. Trình bao bọc này chỉ chấp nhận các đối số từ khóa, có nghĩa là ngay cả khi hàm ban đầu có thể chấp nhận các đối số vị trí, chúng phải được chỉ định bằng tên.

Khi hàm được bao bọc, trình bao bọc cũng tìm kiếm các bộ điều hợp trong chú thích tham số của hàm và áp dụng chúng trước khi chuyển các đối số vào hàm thực.

Khi hàm trả về, trình bao bọc sẽ kiểm tra bộ điều hợp giá trị trả về; nếu nó tìm thấy một, nó sẽ áp dụng nó vào giá trị trả về trước khi cuối cùng trả lại nó.

Khi chúng tôi xem xét tác động của những gì đang xảy ra ở đây, chúng khá ấn tượng. Chúng tôi thực sự đã sửa đổi ý nghĩa của việc truyền một tham số cho một hàm hoặc trả về một giá trị.

Đối số từ khóa

Đôi khi, một hoặc nhiều tham số của phương thức không yêu cầu bất kỳ xử lý nào, ngoại trừ việc gán chúng cho một thuộc tính của chính nó. Chúng tôi có thể sử dụng trình trang trí và chú thích để làm cho điều này xảy ra tự động không? Tất nhiên, chúng tôi có thể.

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