Duomenų patvirtinimas ir klasės

Skip to content

Tai mašinu versta tekstas, kuriame gali būti klaidų!

Python (taip pat ir daugumoje kitų kalbų) galima kurti savus objektus su savomis reikšmėmis, taisyklėmis ir funkcijomis. Tai vadinama klasėmis (angl. classes). Klasės naudojamos susieti susijusius duomenis ir funkcijas į vieną vienetą, pvz., Užsakymas klasę, kuri turi duomenis, tokius kaip užsakymo_id, kliento_vardas, produktai ir funkcijas, tokias kaip pridėti_produktą(), apskaičiuoti_totalą(), ir t.t.

Dictionary (JSON) vs Klasser (Objekter)

JSON (JavaScript Object Notation) yra formatas, skirtas duomenų saugojimui ir perdavimui, nepriklausomai nuo programavimo kalbos, o klasė yra struktūra konkrečioje programavimo kalboje.

Kai turime siųsti duomenis per tinklą arba saugoti juos faile, dažnai naudojame JSON (arba lenteles duomenų bazėse).

Kai turime dirbti su struktūruotais duomenimis kode, naudojame klases.

Šiame modulyje pažiūrėsime, kaip naudoti klases duomenų patikrinimui.

Lengviausia naudoti @dataclassdataclasses bibliotekos. Tai leidžia mums išvengti rašymo daug šabloniško kodo klasės sukūrimui. (Pavyzdžiui, įmontuotų __init__ ir __repr__ (atvaizdavimo) funkcijų).

Pavyzdys klasės be dataclass dekoratoriaus:

class Car:
    def __init__(self, make: str, model: str, year: int):
        self.make = make
        self.model = model
        self.year = year

    def __repr__(self):
        return f"{self.year} {self.make} {self.model}"

my_car = Car("Toyota", "Corolla", 2020)
print(my_car)  # Išvestis: 2020 Toyota Corolla

British python devs be like thats a constructor, __init__?

Pavyzdys su dataclass dekoratorium, kuris pasiekia tą patį, kaip ir aukščiau (bet su mažiau kodo):

from dataclasses import dataclass

@dataclass
class Car:
    make: str
    model: str
    year: int

my_car = Car("Toyota", "Corolla", 2020)
print(my_car)  # Išvestis: Car(make='Toyota', model='Corolla', year=2020)

Easy Užduotis 1 – Sukurkite klasę

Sukurkite klasę pavadinimu Person. Ši klasė turi turėti šiuos atributus:

  • name: Asmens vardas
  • eye_color: Asmens akių spalva
  • phone_number: Asmens telefono numeris
  • email: Asmens el. pašto adresas

Sukurkite (panaudokite) klasės Person objektą su galiojančiomis visų atributų reikšmėmis, kaip parodyta pavyzdyje žemiau.

@dataclass
class Person:
    ... # Tavo kodas čia

bob_kaare = Person(name="Bob Kåre",
                   eye_color="blue",
                   phone_number="12345678",
                   email="bob_kaare@example.com")
print(bob_kaare)

Løsning: En dataclass for Person

Štai galimas sprendimas:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    eye_color: str
    phone_number: str
    email: str

Medium Užduotis 2 – Patvirtinimas klasėje

Prieš tai pateiktame pavyzdyje mes nepradėjome jokio patvirtinimo. Tai reiškia, kad galime sukurti Person su neteisingomis reikšmėmis, pavyzdžiui:

@dataclass
class Person:
    ... # Jūsų kodas čia

invalid_person = Person(name="",
                        eye_color="yes",
                        phone_number="12345",
                        email="not-an-email")
print(invalid_person)
# Output: Person(name='', eye_color='yes', phone_number='12345', email='not-an-email')

Tai (potencialiai) problematiška ir gali sukelti techninę skolą ateityje. Laimei, yra paprastų būdų pridėti klasių validaciją.

Pradėsime nuo el. pašto validacijos. Python turi įmontuotų bibliotekų, kurios gali mums padėti, tačiau kadangi darome tai mokymosi tikslais, sukursime savo paprastą validaciją, sukurdami naują klasę „Email“ ir išnagrinėdami __post_init__ funkciją (tik dataklasei).

Merk

Mes galime tai padaryti ir pačioje Person klasėje, tačiau dažnai geriau kurti atskiras klases daiktams, kuriuos galima pakartotinai naudoti.

Pavyzdys su post_init funkcija
from dataclasses import dataclass

@dataclass
class Email:
    address: str

    def __post_init__(self):
        print(f"Patikrinamas el. paštas: {self.address}")
        # Jūsų kodas čia

Norint atlikti paprastą el. pašto patikrą, galime, pavyzdžiui, patikrinti, ar el. pašte yra tiek @, tiek . ženklai. Galite patikrinti, ar el. paštas atitinka regex modelį. (Sudėtingesnis, bet paieškokite internete!)

Løsning: Kode for enkel e-post validering

Štai galimas sprendimas, naudojame išimtis, kad „sugriautume“ programą, jei el. paštas yra neteisingas, tai iškart sustabdys programą ir pateiks klaidos pranešimą.

from dataclasses import dataclass

@dataclass
class Email:
    address: str

    def __post_init__(self):
        if "@" not in self.address:
            raise ValueError(f"Mangler @ i epostadressen: {self.address}")
        if "." not in self.address.split("@")[1]:
            raise ValueError(f"Mangler . i domenet til epostadressen: {self.address}")
        if " " in self.address:
            raise ValueError(f"Epostadressen kan ikke inneholde mellomrom: {self.address}")

# Test koden 
test = Email("hei@example.com")  # Gyldig
try:
    test = Email("heiexample.com")
except ValueError as e:
    print(e) # Ugyldig, mangler @

Medium Užduotis 3 – Telefonų numerių patvirtinimas

Sukurkite klasę, analogišką sukurtajai el. pašto adresams, tačiau dabar – telefonų numeriams.

Iššūkis su telefono patvirtinimu!

Ar galite pataisyti telefono numerio patvirtinimą, kad jis priimtų tiek raides (str), tiek skaičius (int)? Pavyzdžiui, 12345678 ir "12345678" turi būti abu galiojantys.

Taip pat pabandykite pridėti šalies kodus kaip atributą (klasės subvertę). Pavyzdžiui, 47 Norvegijai, 46 Švedijai.

Medium Užduotis 4 – Validacijos naudojimas Person klasėje

Dabar, kai sukūrėme validaciją el. paštui ir telefono numeriui, galime ją naudoti Person klasėje.

@dataclass
class Person:
    name: str
    eye_color: str
    phone_number: PhoneNumber  # Naudokite PhoneNumber klasę
    email: Email               # Naudokite Email klasę

Naujas iššūkis atsirado!

Dabar, kai pakeitėme Person klasę, kad ji naudotų PhoneNumber ir Email klases, taip pat turime pakeisti, kaip sukuriame (generuojame) Person. Dabar pirmiausia turime sukurti PhoneNumber ir Email objektą, prieš galėdami sukurti Person.

bob_kaare = Person(name="Bob Kåre",
                   eye_color="blue",
                   phone_number=PhoneNumber("12345678"),  # Atkreipkite dėmesį į šią pakeitimą
                   email=Email("bob_kaare@example.com"))  # Atkreipkite dėmesį į šią pakeitimą
print(bob_kaare)

# Pastaba: reikalingas pakeitimas būdų, kaip išgauti vertes
print(bob_kaare.email.address)
print(bob_kaare.phone_number.number)  # .country_code(?)

Hard Užduotis 5 – Klasės savybės (Neprivaloma)

Kai naudojame objektus reikšmėms, tokioms kaip el. paštas ir telefono numeris, turime nurodyti paklasių reikšmę (pvz., address el. paštui ir number telefono numeriui) kiekvieną kartą, kai norime gauti reikšmę. Tai ilgainiui gali tapti šiek tiek varginančiu. Laimei, yra sprendimas, naudojant @property dekoratorių klasėje, leidžiantį gauti reikšmę tiesiogiai iš objekto, nereikalingai nenurodant paklasių reikšmės.

Tačiau tai sukelia dar vieną iššūkį, nes mums reikia __init__ funkcijos Person klasėje. Taip yra todėl, kad negalime naudoti to paties pavadinimo tiek savybei, tiek atributui duomenų klasėje.

from dataclasses import dataclass

@dataclass
class EksempelVerdi:
    attributt: str

@dataclass
class Person:
    name: str
    _verdi: EksempelVerdi  # Vidinis kintamasis (pradedamas _ norint nurodyti, kad jis yra „privatus“)

    def __init__(self, name: str, verdi: EksempelVerdi):
        self.name = name
        self._verdi = verdi

    @property
    def verdi(self):
        return self._verdi.attributt  # Gauna pakaitinį tiesiogiai

# Test koden
person = Person(name="Alice", verdi=EksempelVerdi("Noe tekst"))
print(person.verdi)  # Output: Noe tekst

Merk

Savybės yra unikalios tuo, kad joms nereikia parametrų ir joms nereikia skliaustelių, kad būtų paleistos. Pavyzdyje gauname person.verdi be skliaustelių (ne person.verdi()), nors techniškai tai yra funkcija.

Alternatyvus pavyzdys, priimantis tiek str, tiek EksempelVerdi
@dataclass
class Person:
    name: str
    _verdi: str

    def __init__(self, name: str, verdi: str | EksempelVerdi):
        self.name = name
        if isinstance(verdi, EksempelVerdi):
            self._verdi = verdi
        elif isinstance(verdi, str):
            self._verdi = EksempelVerdi(verdi)
        else:
            raise TypeError("verdi må være av type str eller EksempelVerdi")

    @property
    def verdi(self) -> str:
        return self._verdi.attributt  # Henter ut underverdien direkte

# Test koden
person = Person(name="Alice", verdi="Noe tekst")
print(person.verdi)  # Output: Noe tekst

Hard Užduotis 6 – Savybės su logika (Neprivaloma)

Atnaujinkite Person įtraukdami naują atributą pavadinimu birthday (gimimo diena). Jis turi būti tipo datetime.date (iš datetime bibliotekos).

Tada sukurkite šias savybes:

  • Sukurkite savybę age, kuri apskaičiuoja asmens amžių remiantis birthday ir dabartine data.
  • Sukurkite savybę is_adult, kuri grąžina True, jei asmeniui yra 18 metų arba daugiau, kitaip False.

Løsning: Alder og voksen som properties

Štai galimas sprendimas:

from dataclasses import dataclass
from datetime import date

@dataclass
class Person:
    name: str
    birthday: date

    @property
    def age(self) -> int:
        """Regner ut alderen basert på fødselsdato og dagens dato"""
        today = date.today()
        age = today.year - self.birthday.year
        # Juster ned med 1 hvis personen ikke har hatt bursdag i år ennå
        if (today.month, today.day) < (self.birthday.month, self.birthday.day):
            age -= 1
        return age

    @property
    def is_adult(self) -> bool:
        """Returnerer True hvis personen er 18 år eller eldre"""
        return self.age >= 18

# Test koden
person = Person(name="Alice", birthday=date(2005, 5, 15))
print(person.age)       # F.eks. 18 hvis dagens dato er etter 15. mai 2023
print(person.is_adult)  # True

Dar vienas iššūkis!

Ar galite priversti Person instancijavimą priimti tiek datetime.date, tiek tekstę formatu "DD-MM-YYYY" laukui birthday? (Patarimas: naudokite datetime.strptime, kad konvertuotumėte eilutę į datetime.date)