programowanieProgramista Python

Czym są adnotacje funkcji (function annotations) w Pythonie, jak działa mechanizm type hints, czy wpływają one na wykonywanie kodu w runtime i jakie błędy popełniają nieostrożni programiści?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Historia pytania

Adnotacje funkcji pojawiły się w Pythonie 3.0, a mechanizm type hints (podpowiedzi typów) opisany jest w PEP 484, dodany z wersją 3.5. Narzędzie zostało stworzone do statycznej analizy kodu, autouzupełniania i zwiększania czytelności — standardowa biblioteka typów (typing) pozwala na jawne wskazywanie oczekiwanych typów zmiennych, argumentów oraz wartości zwracanych przez funkcje.

Problem

Python to język dynamiczny, w którym typy zmiennych mogą zmieniać się w trakcie wykonywania, co może prowadzić do błędów pojawiających się tylko w trakcie działania programu. Adnotacje nie wpływają na wykonywanie kodu, ale przy niewłaściwym ich użyciu programiści mogą mieć błędne poczucie "ścisłej typizacji".

Rozwiązanie

Adnotacje typów stosuje się w dokumentacji, automatycznym sprawdzaniu za pomocą mypy, pylance, pyright i podobnych narzędzi, a także do integracji z IDE. Realizowane są przez dwukropek po nazwie argumentu oraz strzałkę po liście parametrów:**

def greet(name: str, times: int = 1) -> None: for _ in range(times): print(f"Hello, {name}!") # Poprawna adnotacja dla funkcji przetwarzającej słownik from typing import Dict, List def transform(data: Dict[str, List[int]]) -> float: return sum(sum(lst) for lst in data.values()) / 10

Kluczowe cechy:

  • Adnotacje nie są sprawdzane przez interpreter, pozostają podczas wykonywania kodu, ale w żaden sposób nie zmieniają jego wykonania
  • Mają znaczenie w dużych projektach, gdzie ważne jest zrozumienie interfejsów i interakcji komponentów
  • Do złożonych konstrukcji potrzebne są typing.List, typing.Dict, typing.Optional, typing.Union itp.

Pytania z haczykiem.

Czy Python może "automatycznie" sprawdzać zgodność z typami zadeklarowanymi w adnotacjach?

Nie! Sprawdzanie typów odbywa się tylko za pomocą zewnętrznych narzędzi do analizy statycznej, np. mypy. W czasie wykonywania Python całkowicie ignoruje zawartość adnotacji.

def f(x: int): return x * 2 print(f('oops')) # Typ str, błędu nie będzie!

Gdzie przechowywane są adnotacje i jak można je uzyskać w czasie wykonywania, po co to może być potrzebne?

Przechowywane są w specjalnym atrybucie annotations:

def add(x: int, y: int) -> int: return x + y print(add.__annotations__) # {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

To wykorzystują zewnętrzne biblioteki do walidacji danych, autogeneracji dokumentacji, serializacji itp.

Czy można adnotować dowolną zmienną, tylko funkcje, co będzie w zakresie globalnym?

Można adnotować zarówno lokalne, jak i globalne zmienne przez dwukropek, to także nie wpływa na wykonanie:

index: int = 0 def func(x: 'User') -> None: ...

Typowe błędy i antywzorce

  • Sądzic, że type hints to część ścisłej typizacji i ścisłej kontroli typów
  • Zapominać, że wartości domyślne muszą być zgodne z zadeklarowanym typem (choć nie jest to sprawdzane w trakcie wykonania)
  • Niewłaściwe użycie złożonych typów z typing (np. List<int> zamiast List[int])

Przykład z życia

Negatywny przypadek

W projekcie korporacyjnym wszyscy programiści zaczęli aktywnie wdrażać adnotacje, a rzeczywiste typy argumentów funkcji często nie zgadzały się z podanymi. Python przepuszczał te błędy, a nieoczekiwane błędy pojawiały się tylko w głębi logiki biznesowej. Brakowało konfiguracji mypy.

Zalety:

  • Poprawa autouzupełnienia i dokumentacji

Wady:

  • Pozostały niejawne błędy, które oddalały przyczynę daleko od miejsca adnotacji

Pozytywny przypadek

Wykorzystanie type hints i obowiązkowe uruchamianie mypy w CI, a także autogeneracja dokumentacji przez annotations:

Zalety:

  • Minimalizacja błędów z niezgodnościami typów
  • Zwiększenie jakości współpracy nad API

Wady:

  • Pojawia się dodatkowa praca związana z utrzymywaniem aktualności adnotacji w trakcie refaktoryzacji