Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Python

Function Annotations (Chú thích hàm) trong Python

Function annotations được giới thiệu trong Python 3.0, cho phép thêm metadata tùy ý cho tham số hàm và giá trị trả về. Kể từ Python 3, function annotations chính thức được thêm vào (PEP-3107). Mục đích chính: cung cấp cách chuẩn để liên kết metadata với tham số và giá trị trả về.

Cơ bản về Function Annotations

  • Annotations hoàn toàn tùy chọn cho cả tham số và giá trị trả về.
  • Cung cấp cách liên kết các phần của hàm với biểu thức Python tùy ý tại compile time.
  • PEP-3107 không cố gắng đưa ra ngữ nghĩa chuẩn, ngay cả cho built-in types. Toàn bộ việc này để cho thư viện bên thứ ba.

Cú pháp

Annotations cho tham số đơn giản

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

Annotations cho tham số thừa (excess parameters)

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

Tham số lồng nhau (nested parameters)

Annotations luôn theo sau tên tham số, không phải sau ngoặc cuối. Không bắt buộc annotate tất cả tham số lồng nhau.

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

Python không cung cấp ngữ nghĩa với annotations. Chỉ cung cấp hỗ trợ cú pháp để liên kết metadata và cách truy cập dễ dàng. Annotations không bắt buộc.

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

Ví dụ: func() nhận 3 tham số x, y, z, in tổng. Tham số đầu x annotate chuỗi 'annotating x', y annotate 'annotating y', z annotate kiểu int. Giá trị trả về annotate kiểu float. Cú pháp -> dùng cho annotate giá trị trả về.

Kết quả

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

Gọi func() hai lần: một lần với int, một lần với string. Cả hai đều chạy đúng, annotations bị bỏ qua. Annotations không ảnh hưởng đến thực thi hàm.

Truy cập Function Annotations

Tất cả annotations lưu trong dictionary __annotations__, 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': , 'return': }

Annotations không phải khai báo kiểu, tuy nhiên có thể dùng cho mục đích đó và giống cú pháp typing trong ngôn ngữ khác.

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

Chúng là biểu thức tùy ý, nghĩa là giá trị tùy ý có thể lưu trong __annotations__. Mặc dù không thêm ý nghĩa cho Python, nhưng định nghĩa kiểu tham số và trả về là trường hợp sử dụng phổ biến.

Decorator @no_type_check

Nếu dùng công cụ giả định annotations là khai báo kiểu nhưng muốn dùng cho mục đích khác, dùng decorator chuẩn @no_type_check để miễn trừ hàm khỏi xử lý đó:

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

Annotations làm đầu vào cho Function Decorators

Annotations kết hợp tốt với decorators vì giá trị annotation cung cấp cách tốt để truyền đầu vào cho decorator, và wrapper do decorator tạo là nơi tốt để đặt code gán nghĩa cho annotations.

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

Decorator adapted bao hàm hàm trong wrapper. Wrapper chỉ chấp nhận keyword arguments, nghĩa là ngay cả khi hàm gốc chấp nhận positional arguments, chúng phải được chỉ định bằng tên.

Keyword Arguments

Đôi khi một hoặc nhiều tham số của phương thức không cần xử lý ngoài việc gán cho thuộc tính của self. Có thể dùng decorators và annotations để tự động hóa?

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