Tôi nên sử dụng gợi ý loại Tùy chọn như thế nào?


84

Tôi đang cố gắng hiểu cách sử dụng Optionalgợi ý loại. Từ PEP-484 , tôi biết tôi có thể sử dụng Optionalcho def test(a: int = None)cả hai như def test(a: Union[int, None])hay def test(a: Optional[int]).

Nhưng làm thế nào về các ví dụ sau đây?

def test(a : dict = None):
    #print(a) ==> {'a': 1234}
    #or
    #print(a) ==> None

def test(a : list = None):
    #print(a) ==> [1,2,3,4, 'a', 'b']
    #or
    #print(a) ==> None

Nếu Optional[type]dường như có nghĩa giống như Union[type, None], tại sao tôi nên sử dụng Optional[]?

Câu trả lời:


120

Optional[...]là một ký hiệu viết tắt cho Union[..., None], cho người kiểm tra kiểu biết rằng một đối tượng của kiểu cụ thể là bắt buộc hoặc None được yêu cầu. ...là viết tắt của bất kỳ gợi ý loại hợp lệ nào , bao gồm các loại hợp chất phức tạp hoặc Union[]nhiều loại hơn. Bất cứ khi nào bạn có một đối số từ khóa với giá trị mặc định None, bạn nên sử dụng Optional.

Vì vậy, đối với hai ví dụ của bạn, bạn có dictlistcác loại vùng chứa, nhưng giá trị mặc định cho ađối số từ khóa cho thấy điều đó Nonecũng được phép, vì vậy hãy sử dụng Optional[...]:

from typing import Optional

def test(a: Optional[dict] = None) -> None:
    #print(a) ==> {'a': 1234}
    #or
    #print(a) ==> None

def test(a: Optional[list] = None) -> None:
    #print(a) ==> [1, 2, 3, 4, 'a', 'b']
    #or
    #print(a) ==> None

Lưu ý rằng về mặt kỹ thuật không có sự khác biệt giữa việc sử dụng Optional[]trên a Union[]hoặc chỉ thêm Nonevào Union[]. Vì vậy Optional[Union[str, int]]Union[str, int, None]chính xác là những điều giống nhau.

Cá nhân, tôi muốn sử dụng luôn luôn sử dụng Optional[]khi đặt loại cho đối số từ khóa sử dụng = Noneđể đặt giá trị mặc định, điều này ghi lại lý do tại sao Noneđược cho phép tốt hơn. Hơn nữa, nó giúp dễ dàng di chuyển Union[...]phần đó thành bí danh kiểu riêng biệt hoặc sau đó xóa Optional[...]phần đó nếu một đối số trở nên bắt buộc.

Ví dụ, giả sử bạn có

from typing import Optional, Union

def api_function(optional_argument: Optional[Union[str, int]] = None) -> None:
    """Frob the fooznar.

    If optional_argument is given, it must be an id of the fooznar subwidget
    to filter on. The id should be a string, or for backwards compatibility,
    an integer is also accepted.

    """

sau đó tài liệu được cải thiện bằng cách kéo Union[str, int]bí danh thành một loại:

from typing import Optional, Union

# subwidget ids used to be integers, now they are strings. Support both.
SubWidgetId = Union[str, int]


def api_function(optional_argument: Optional[SubWidgetId] = None) -> None:
    """Frob the fooznar.

    If optional_argument is given, it must be an id of the fooznar subwidget
    to filter on. The id should be a string, or for backwards compatibility,
    an integer is also accepted.

    """

Bộ tái cấu trúc để chuyển Union[]thành bí danh trở nên dễ dàng hơn nhiều vì Optional[...]đã được sử dụng thay vì Union[str, int, None]. Các Nonegiá trị không phải là một 'id subwidget' sau khi tất cả, nó không phải là một phần của giá trị, Nonecó nghĩa là để cờ sự vắng mặt của một giá trị.

Lưu ý bên cạnh: Trừ khi mã của bạn chỉ phải hỗ trợ Python 3.9 hoặc mới hơn, bạn muốn tránh sử dụng các loại vùng chứa thư viện tiêu chuẩn trong gợi ý loại, vì bạn không thể nói bất kỳ điều gì về loại chúng phải chứa. Vì vậy, thay vì dictlist, sử dụng typing.Dicttyping.List, tương ứng. Và khi chỉ đọc từ một loại vùng chứa, bạn cũng có thể chấp nhận bất kỳ loại vùng chứa trừu tượng bất biến nào; danh sách và bộ giá trị là Sequencecác đối tượng, trong khi dictlà một Mappingloại:

from typing import Mapping, Optional, Sequence, Union

def test(a: Optional[Mapping[str, int]] = None) -> None:
    """accepts an optional map with string keys and integer values"""
    # print(a) ==> {'a': 1234}
    # or
    # print(a) ==> None

def test(a: Optional[Sequence[Union[int, str]]] = None) -> None:
    """accepts an optional sequence of integers and strings
    # print(a) ==> [1, 2, 3, 4, 'a', 'b']
    # or
    # print(a) ==> None

Trong Python 3.9 trở lên, tất cả các loại vùng chứa tiêu chuẩn đều đã được cập nhật để hỗ trợ việc sử dụng chúng trong các gợi ý loại, hãy xem PEP 585 . Tuy nhiên , mặc dù bây giờ bạn có thể sử dụng dict[str, int]hoặc list[Union[int, str]], bạn vẫn có thể muốn sử dụng các chú thích Mappingvà biểu cảm hơn Sequenceđể chỉ ra rằng một hàm sẽ không thay đổi nội dung (chúng được coi là 'chỉ đọc') và các hàm sẽ hoạt động với bất kỳ đối tượng nào hoạt động dưới dạng ánh xạ hoặc chuỗi tương ứng.


@MartijnPieters Chúng ta không cần nhập DictListtừ nhập và viết Optional[Dict]Optional[List]thay vì Optional[dict]...
Alireza

@Alireza có, và tôi đã nêu rõ điều đó trong câu trả lời của mình. Tìm kiếm: Lưu ý phụ: Tuy nhiên, bạn muốn tránh sử dụng các loại vùng chứa thư viện tiêu chuẩn trong gợi ý loại, vì bạn không thể nói bất kỳ điều gì về loại chúng phải chứa
Martijn Pieters

Hãy sửa tôi nếu tôi sai, nhưng 3.9 cho phép listdictđược sử dụng cho các gợi ý loại, (so với List, Dict). python.org/dev/peps/pep-0585
user48956

2
@ user48956: Tôi đã thêm một phần trên 3.9.
Martijn Pieters

3

Trực tiếp từ tài liệu mô-đun gõ mypy .

  • “[Str] tùy chọn chỉ là cách viết tắt hoặc bí danh của Union [str, None]. Nó tồn tại chủ yếu như một sự tiện lợi để giúp các chữ ký chức năng trông gọn gàng hơn một chút. "
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.