Рубрики
Без рубрики

Annotations Type Annotations 📝- почему вы всегда должны использовать Это

Python является динамически напечатанным языком и позволяет нам довольно свободно работать на переменных Diffe … Помечено Python, Web Dev, Data Science, компьютерной наукой.

Python является динамически напечатанным языком и позволяет нам довольно свободно работать на переменных разных типов. Однако при написании кода мы каким -то образом предполагаем, какие типы переменных будут использоваться (это может быть вызвано ограничением алгоритма или бизнес -логики). И для того, чтобы программа была правильно работала, для нас важно найти ошибки, связанные с передачей данных неправильного типа как можно раньше.

Сохранение идеи динамического набора уток в современных версиях Python (3,6+) Поддерживает аннотации типов переменных, полей классов, аргументов и возвращаемых значений функций:

Аннотации типа читаются интерпретатором Python и не обрабатываются каким-либо образом, но доступны для использования из стороннего кода и в основном предназначены для статических анализаторов.

В этой статье я хочу объяснить основы и привести примеры использования типовых аннотаций и в конечном итоге показать, почему это сделало мою жизнь в качестве разработчика Python намного проще 🙂.

Во -первых, давайте поймем, какие аннотации типа находятся

Сами типы используются для указания основных типов переменных:

  • стр
  • инт
  • плавать
  • буль
  • сложный
  • байты
  • и т. д.

В отличие от более старых версий Python, типовые аннотации не написаны в комментариях или Docstrings, а непосредственно в коде. С одной стороны, это ломается назад совместимость. С другой стороны, это ясно означает, что это является частью кода и может быть обработан соответствующим образом.

В простейшем случае аннотация содержит непосредственно ожидаемый тип. Более сложные случаи будут обсуждаться ниже. Если базовый класс указан как аннотация, он может передавать экземпляры своих потомков в качестве значений. Тем не менее, вы можете использовать только те возможности, которые реализованы в базовом классе.

Переменные аннотации написаны с помощью толстой кишки после идентификатора. Это может сопровождаться инициализацией значения. Например:

price: int = 5
title: "str"

Параметры функции аннотируются так же, как и переменные, и возвратное значение указывается после стрелки -> и до затяжной толстой кишки. Позвольте мне привести пример использования аннотаций типа в функции Python:

def func(a: int, b: float) -> str:
    a: str = f"{a}, {b}"
    return a

Для полей классов аннотации должны быть указаны явно при определении класса. Однако анализаторы могут автоматически вывести их на основе __init__ Метод, но в этом случае они не будут доступны во время выполнения:

class Book:
    title: "str"
    author: str

    def __init__(self, title: "str, author: str) -> None:"
        self.title = title
        self.author = author

b: Book = Book(title="Fahrenheit 451", author="Bradbury")

Хотя вы можете использовать стандартные типы в качестве аннотаций, в модуле есть много полезных вещей набор Анкет Давайте посмотрим на его подмодулы.

Если вы отмечаете переменную с помощью типа int и попытаться назначить ему нет, будет ошибка:

Несовместимые типы в присвоении (выражение имеет тип «Нет», переменная имеет тип «int»)

Точно для таких случаев модуль для печати обеспечивает аннотацию Необязательно указав определенный тип. Обратите внимание, что тип необязательной переменной указан в Квадратные кронштейны :

from typing import Optional

amount: int
amount: None # Gives "Incompatible types" error

price: Optional[int]
price: None # Will work!

Иногда вы не хотите ограничивать возможные типы переменной. Например, если это действительно не имеет значения, или если вы планируете выполнять различные виды обработки себя. В этом случае аннотация может быть использована Любой Анкет Он не будет ругаться на следующем коде:

some_item: Any = 1
print(some_item)
print(some_item.startswith("hello"))
print(some_item // 0)

Может возникнуть вопрос, почему бы не использовать объект ? Однако в этом случае предполагается, что, хотя любой объект может быть передан, его можно рассматривать только как экземпляр объект :

some_object: object
print(some_object)
print(some_object.startswith("hello)) # ERROR: "object" has no attribute "startswith"

print(some_object // 0) # ERROR: Unsupported operand types for // ("object" and "int")

Для случаев, когда необходимо разрешить использование не всех типов, но только некоторые, вы можете использовать аннотацию набор Союз Указывая список типов в квадратных скобках.

def hundreds(x: Union[int, float]) -> int:
    return (int(x) // 100) % 100

hundreds(100.0)
hundreds(100)
hundreds("100")
# ERROR: Argument 1 to "hundreds" has incompatible type "str"; expected "Union[int, float]"

Кстати, аннотация Необязательно [t] эквивалентен Союз [t, нет] , хотя такая нотация не рекомендуется.

Механизм типовых аннотаций поддерживает механизм дженериков ( PEP484-Generics , для более подробной информации во второй части статьи), что позволяет указать типы элементов, хранящихся в них для контейнеров.

Чтобы указать, что переменная содержит список, вы можете использовать тип списка в качестве аннотации. Однако, если вы хотите указать, какие элементы содержит список, такая аннотация больше не будет работать. Для этого есть набор Список Анкет Подобно тому, как мы указали тип необязательной переменной, мы указываем тип элементов списка в квадратных скобках.

titles: List[str] = ["hello", "world"]
titles.append(100500)
# ERROR: Argument 1 to "hundreds" has incompatible type "str"; expected "Union[int, float]"

titles = ["hello", 1]
# ERROR: List item 1 has incompatible type "int"; expected "str"

items: List = ["hello", 1]
# Everything is good!

Предполагается, что список содержит неопределенное количество аналогичных элементов. Но в то же время нет никаких ограничений на элементы аннотации: вы можете использовать Любой , Необязательно , Список , и другие. Если тип элемента не указан, предполагается, что он является Любой Анкет

В дополнение к списку есть аналогичные аннотации для наборов: набор Установить и набор Замороженное Анкет

Клетки, в отличие от списков, часто используются для различных типов элементов. Синтаксис похож на одно отличие: тип каждого элемента кортежа указан в квадратных скобках отдельно.

Если вы планируете использовать кортеж, аналогичный списку: хранить неизвестное количество элементов того же типа, вы можете использовать Ellipsis ( ... ).

Аннотация Кортеж Без указания типов элементов работает так же, как Кортеж [любое, ...]

price_container: Tuple[int] = (1,)
price_container: ("hello")
# ERROR: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int]")

price_container = (1, 2)
# ERROR: Incompatible types in assignment (expression has type "Tuple[int, int]", variable has type "Tuple[int]")

price_with_title: Tuple[int, str] = (1, "hello")
# Everything is good!

prices: Tuple[int, ...] = (1, 2)
prices: (1,)
prices: (1, "str")
# ERROR: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "Tuple[int]")

something: Tuple = (1, 2, "hello")
# Everything is good!

Используется для словарей набор Дикта Анкет Тип ключа и тип значения аннотируются отдельно:

book_authors: Dict[str, str] = {"Fahrenheit 451": "Bradbury"}
book_authors["1984"] = 0
# ERROR: Incompatible types in assignment (expression has type "int", target has type "str")

book_authors[1984] = "Orwell"
# ERROR: Invalid index type "int" for "Dict[str, str]"; expected type "str"

Точно так же используется набор DefaultDict и набор Заказан

Любые аннотации типа могут использоваться для указания результата типа функции. Но есть несколько особых случаев.

Если функция ничего не возвращает (например, как print ), ее результат всегда равен Нет Анкет Мы также используем для аннотации Нет Анкет

Правильные параметры для выполнения такой функции: явное возвращение Нет , вернуть без указания значения и прекращения без вызова возврат :

def nothing(a: int) -> None:
    if a == 1:
        return
    elif a == 2:
        return
    elif a == 3:
        return "" # No return value expected
    else:
        pass

Если функция никогда Возвращает контроль (например, как sys.exit ), используйте аннотацию Нортерн :

def forever() -> NoReturn:
    while True:
        pass

Если это функция генератора, то есть его тело содержит выход оператора, вы можете использовать аннотацию для возвращенного Итерабильный [t] , либо Генератор [yt, st, rt] :

def generate_two() -> Iterable[int]:
    yield 1
    yield "2"
    # ERROR: Incompatible types in "yield" (actual type "str", expected type "int")

Во многих ситуациях модуль для печати имеет подходящие типы, но я не буду охватывать все, поскольку поведение аналогично описанному. Например, там Итератор это общая версия для Collections.abc. Итератор , набор SupportSint Чтобы указать, что объект поддерживает метод __int__ или Callible Для функций и объектов, которые поддерживают метод __вызов__

Стандарт также определяет формат аннотаций в форме комментариев и опорных изделий, которые содержат информацию только для статических анализаторов.

Если вы нашли эту статью полезной, нажмите кнопку ниже или 👏 👏 ниже или поделитесь статьей на Facebook, чтобы ваши друзья тоже могли извлечь выгоду из нее.

https://subscribe.to/raevskymichail

Оригинал: “https://dev.to/mikhailraevskiy/python-s-type-annotations-why-you-always-should-use-it-4lh2”