Wymuszanie typów w konstruktorze

Wymuszanie typów w konstruktorze
PA
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 138
0

Hej. Dopiero zaczynam z Pythonem i do tej pory nie miałem za dużo do czynienia z dynamicznym typowaniem. Czy faktycznie jeśli chcę w konstruktorze mieć zabezpieczenie przed stworzeniem obiektu z niewłaściwym typem danych, to powinienem do każdego pola dodawać:

Kopiuj
class Book:
    def __init__(self, title: str, author: str, year: int, isbn: str, genre: str):
        if not isinstance(title, str):
            raise TypeError("Title must be a string")
        if not isinstance(author, str):
            raise TypeError("Author must be a string")
        if not isinstance(year, int):
            raise TypeError("Year must be an integer")
        if not isinstance(isbn, str):
            raise TypeError("ISBN must be a string")
        if not isinstance(genre, str):
            raise TypeError("Genre must be a string")
        
        self.title = title
        self.author = author
        self.year = year
        self.isbn = isbn
        self.genre = genre

tak jak mi radzi GPT?

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6967
3

Jak dla mnie seria wywołań isinstance() to paranoja.
Już lepiej użyj normalny język ze statycznym typowaniem. Np. C#.

Zapytałem się chata, czy można bez isinstance().
Oto co odpisał:

W Pythonie możesz sprawdzać typy bez użycia isinstance() poprzez wykorzystanie adnotacji typów (ang. type hints) w połączeniu z narzędziami takimi jak mypy lub Pyright, które statycznie sprawdzają poprawność typów podczas tworzenia kodu. Dzięki temu błędy związane z typami można wykryć przed uruchomieniem programu.

1. Adnotacje typów:

W Pythonie możesz dodać adnotacje typów dla argumentów funkcji i zwracanych wartości, np.:

Kopiuj
def dodaj_liczby(a: int, b: int) -> int:
    return a + b

Tutaj funkcja dodaj_liczby oczekuje, że oba argumenty będą typu int i zwróci wartość typu int. Jednak same adnotacje nie wymuszają typów podczas działania programu.

2. Statyczna weryfikacja typów z mypy:

Możesz użyć narzędzia takiego jak mypy, aby wymusić poprawność typów na podstawie adnotacji. Kiedy napiszesz kod z adnotacjami, możesz uruchomić mypy, aby sprawdzić, czy kod jest zgodny z oczekiwanymi typami.

Aby używać mypy:

  1. Zainstaluj mypy:
    Kopiuj
    pip install mypy
    
  2. Uruchom sprawdzanie typów:
    Kopiuj
    mypy twoj_plik.py
    

Przykład:

Kopiuj
# twoj_plik.py
def dodaj_liczby(a: int, b: int) -> int:
    return a + b

dodaj_liczby(1, 2)    # Poprawne wywołanie
dodaj_liczby(1, '2')  # mypy zgłosi błąd, bo '2' to string

Kiedy uruchomisz mypy, zgłosi ono błąd dla drugiego wywołania, ponieważ '2' jest typu str, a nie int.

3. Egzekwowanie typów w czasie działania (opcjonalne):

Chociaż Python nie wymusza typów w czasie działania (bo to język dynamicznie typowany), możesz użyć bibliotek takich jak pydantic, enforce czy typeguard, aby weryfikować typy na podstawie adnotacji również w czasie działania.

Przykład z typeguard:

Najpierw instalujesz bibliotekę:

Kopiuj
pip install typeguard

A następnie korzystasz z niej, aby sprawdzać typy podczas działania programu:

Kopiuj
from typeguard import typechecked

@typechecked
def dodaj_liczby(a: int, b: int) -> int:
    return a + b

dodaj_liczby(1, 2)    # Działa poprawnie
dodaj_liczby(1, '2')  # Podczas działania zgłosi TypeError

W przypadku podania błędnych typów, typeguard podniesie wyjątek TypeError podczas działania programu.

Podsumowanie:

  • Adnotacje typów pomagają w statycznym sprawdzaniu typów.
  • Narzędzia takie jak mypy lub Pyright umożliwiają statyczne wymuszanie poprawności typów.
  • Dla wymuszenia typów w czasie działania można opcjonalnie użyć bibliotek takich jak typeguard.
YA
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2384
2

Oprócz tego co napisał @Spine, możesz śledzić https://peps.python.org/topic/typing/ i wyrobić sobie pogląd, czego się spodziewać w przyszłości.

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10227
1
paranoise napisał(a):

Czy faktycznie jeśli chcę w konstruktorze mieć zabezpieczenie przed stworzeniem obiektu z niewłaściwym typem danych, to powinienem do każdego pola dodawać: [...]

Taka zagrywka nie za bardzo ma sens. Dużo lepiej jest napisać testy pod kod który tworzy instację Book.

Poza tym, to też nie jest tak że zrobisz w ten sposób statyczne typowanie. Python jest silnie i dynamicznie typowany. Błąd typów tak czy tak będzie w runtime, nie ważne czy zrobisz takiego checka w konstruktorze czy nie.

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
1

Popatrz na to, to nie wygląda jak Python, to nie wygląda jak kod nawet, dla mnie to jest stack trace 🙃

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2002
0

Nie jestem na bieżąco, Python nie dorobił się jeszcze wersji runtime, flagi albo czegokolwiek co te typy wymusza automatycznie?

Drzewiej sam smarowałem w zbliżony sposób, ale to było uciążliwe i generalnie się nie broniło. W ostateczności udało się przynajmniej zapiąć jakiegoś pylint żeby krzyczał, jeśli wykryje jawne naruszenie type hintów, ale to nie było 100% skuteczne.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6967
0

@superdurszlak: Automatycznie pewnie można liczyć na to, że IDE będzie krzyczało podczas pracy nad kodem, np. PyCharm:
https://www.jetbrains.com/help/pycharm/type-hinting-in-product.html#validate-type-hints

99xmarcin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2420
1

IDE nie wepnę do CI jako build breakera 😉

Nie wiem jak teraz ale jeszcze kilka lat temu .NET na CI wymagał instalacji pełnego Visual Studio (full GUI)...

99xmarcin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2420
1

Typowanie jest dynamiczne: if it walks like a duck then it is a duck. Nie trzeba sprawdzać czy to int wystarczy że obsłuży dodawanie itp...

Najlepiej weź jakiś dobry projekty w Pythonie np. bibliotekę standardową i zobacz jak oni to robią: https://github.com/python/cpython/blob/main/Lib/timeit.py#L105

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
1

Nie no, nie róbmy z Pythona C#.

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1022
0

Ma to sens jak:

  • piszesz bibliotekę
  • twoi użytkownicy nie sprawdzają typowania

W takim przypadku taka walidacja jest pomocna. Kiedyś to był jedyny sposób, więc pewnie dlatego ChatGPT wygenerował ten kod

Jeśli piszesz kod, który nie jest biblioteką i kontrolujesz toolkit to używaj typów i tyle

ledi12
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
2

Python nigdy nie był i nie będzie statycznie typowany, bo nie taka jest jego rola i przeznaczenie. Masz libke typing która działa read-only (możesz zadeklarować coś jako string a i tak interpreter przepuści inta, jedynie linia się podkreśli czerwono w ide).

Jesli już tak bardzo chcesz rzucać wyjątek dla złych typów to zainteresuj się pydanticem. Ew napisz customowy deskryptor :)

Kopiuj

class TypedAttribute:
    def __init__(self, expected_type):
        self.expected_type = expected_type
        self.data = {}

    def __get__(self, instance, owner):
        return self.data.get(instance)

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(f"Expected {self.expected_type.__name__} but got {type(value).__name__}")
        self.data[instance] = value

    def __delete__(self, instance):
        del self.data[instance]

class Book:
    title = TypedAttribute(str)
    author = TypedAttribute(str)
    year = TypedAttribute(int)
    isbn = TypedAttribute(str)
    genre = TypedAttribute(str)

    def __init__(self, title, author, year, isbn, genre):
        self.title = title
        self.author = author
        self.year = year
        self.isbn = isbn
        self.genre = genre
Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10227
0
ledi12 napisał(a):

Python nigdy nie był i nie będzie statycznie typowany, bo nie taka jest jego rola i przeznaczenie.

Ale to co autor postu chce to nie jest statyczne typowanie 😐 Autor chciałby żeby typ zmiennej został sprawdzony w runtime, podczas wołania konstruktora, więc to nadal jest dynamiczne typowanie.

Jemu chodzi o takie dynamiczne typy jak w PHP, czyli np function(): bool { return 1; }, czyli program się włączy normalnie, ale przy wywołaniu funkcji rzuci wyjątek w runtime.

Statyczne typowanie to byłoby takie coś, że program się nawet niewłączy/nie skompiluje jak typy są niepoprawne.

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
0

Tak patrząc, to chyba jednak mypy będzie najlepszym wyborem.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.