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 @dataclass iš dataclasses 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
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)
Užduotis 1 – Sukurkite klasę
Sukurkite klasę pavadinimu Person. Ši klasė turi turėti šiuos atributus:
name: Asmens vardaseye_color: Asmens akių spalvaphone_number: Asmens telefono numerisemail: 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
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 @
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.
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(?)
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
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ų remiantisbirthdayir dabartine data. - Sukurkite savybę
is_adult, kuri grąžinaTrue, jei asmeniui yra 18 metų arba daugiau, kitaipFalse.
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)
