Це машинний переклад, який може містити помилки!
Валідація даних і класи
У Python (це також стосується більшості інших мов) можна створювати власні об’єкти зі своїми значеннями, правилами та функціями. Це називають класами (англійською — classes). Ми використовуємо класи для збору пов’язаних даних і функцій в єдину одиницю, наприклад, клас Ordre, який містить дані, такі як ordre_id, kunde_navn, produkter, а також функції, такі як legg_til_produkt(), beregn_total() тощо.
Словник (JSON) та класи (об’єкти)
JSON (JavaScript Object Notation) — це формат для зберігання та передачі даних незалежно від мови програмування, тоді як клас є структурою в конкретній мові програмування.
Коли ми маємо надсилати дані через мережу або зберігати їх у файлі, ми часто використовуємо JSON (або таблиці в базах даних).
Коли ми працюємо зі структурованими даними в коді, ми використовуємо класи.
У цьому модулі ми розглянемо, як можна використовувати класи для валідації даних.
Відео: Як класи збирають дані та правила
У відео пояснюється, що таке клас, і як класи можна використовувати для структурування та валідації даних.
Найпростіше використання — це використати @dataclass із бібліотеки dataclasses. Це дозволяє нам уникнути написання великої кількості шаблонного коду для створення класу. (Наприклад, вбудовані функції __init__ та __repr__ (представлення)).
Приклад класу без використання декоратора dataclass:
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) # Вивід: 2020 Toyota Corolla
Приклад із декоратором dataclass, який досягає того ж самого результату, що і вище (але з меншим обсягом коду):
from dataclasses import dataclass
@dataclass
class Car:
make: str
model: str
year: int
my_car = Car("Toyota", "Corolla", 2020)
print(my_car) # Вивід: Car(make='Toyota', model='Corolla', year=2020)
Завдання 1 - Створіть клас
Створіть клас з назвою Person. Цей клас повинен мати такі властивості (атрибути):
name: Ім’я особиeye_color: Колір очей особиphone_number: Номер телефону особиemail: Електронна адреса особи
Створіть екземпляр (обʼєкт) класу Person, встановивши для всіх властивостей дійсні значення, як у наведеному нижче прикладі.
@dataclass
class Person:
... # Тут ваш код
bob_kaare = Person(name="Bob Kåre",
eye_color="blue",
phone_number="12345678",
email="bob_kaare@example.com")
print(bob_kaare)
Рішення: Dataclass для Person
Ось можливе рішення:
from dataclasses import dataclass
@dataclass
class Person:
name: str
eye_color: str
phone_number: str
email: str
Завдання 2 - Валідація в класі
У прикладі вище ми не додали жодної валідації. Це означає, що ми можемо створити Person з недійсними значеннями, такими як:
@dataclass
class Person:
... # Тут ваш код
invalid_person = Person(name="",
eye_color="yes",
phone_number="12345",
email="not-an-email")
print(invalid_person)
# Вивід: Person(name='', eye_color='yes', phone_number='12345', email='not-an-email')
Це (потенційно) проблематично і може швидко призвести до технічного боргу у майбутньому. На щастя, існують прості способи додавати валідацію в класи.
Ми почнемо з розгляду валідації електронної пошти. У Python є вбудовані бібліотеки, які можуть допомогти нам із цим, але оскільки ми робимо це для навчання, то створимо власну просту валідацію, створивши новий клас «Email» та досліджуючи функцію __post_init__ (лише для dataclass).
Примітка
Ми можемо також зробити це в самому класі Person, але часто краще створювати окремі класи для речей, які можна використовувати повторно.
Приклад функції __post_init__
from dataclasses import dataclass
@dataclass
class Email:
address: str
def __post_init__(self):
print(f"Перевірка електронної пошти: {self.address}")
# Ваш код тут
Для простого валідації електронної пошти ми можемо, наприклад, перевірити, чи містить адреса як символ @, так і крапку . Також ви можете переконатися, що адреса відповідає регулярному виразу. (Більш складний підхід, але сміливо шукайте в інтернеті!)
Розв’язка: Код для простої валідації електронної пошти
Ось можливе рішення. Ми використовуємо винятки (exceptions), щоб «зламати» програму, якщо адреса електронної пошти є недійсною; це призведе до негайного зупинення програми та виведення повідомлення про помилку.
from dataclasses import dataclass
@dataclass
class Email:
address: str
def __post_init__(self):
if "@" not in self.address:
raise ValueError(f"Відсутній символ @ у адресі ел. пошти: {self.address}")
if "." not in self.address.split("@")[1]:
raise ValueError(f"Відсутній крапка . у домені адреси ел. пошти: {self.address}")
if " " in self.address:
raise ValueError(f"Адреса ел. пошти не може містити пробіли: {self.address}")
# Тестування коду
test = Email("hei@example.com") # Дійсна
try:
test = Email("heiexample.com")
except ValueError as e:
print(e) # Недійсний, відсутній @
Завдання 3 — Валідація номеру телефону
Створіть клас аналогічно до того, який ви створювали для електронних поштових адрес, але тепер для телефонного номера
Виклик щодо верифікації телефону!
Чи можете ви виправити валідацию номера телефону так, щоб вона приймавала як літери (стр), так і цифри (інт)? Наприклад, 12345678 та "12345678" мають бути обидва дійсними.
Також спробуйте додати коди країн як атрибут (підзначення класу). Наприклад, 47 для Норвегії, 46 для Швеції
Завдання 4 – Використання валідації в класі Person
Тепер, коли ми створили валідацію для електронної пошти та номера телефону, ми можемо використовувати їх у класі Person.
@dataclass
class Person:
name: str
eye_color: str
phone_number: PhoneNumber # Використовуйте клас PhoneNumber
email: Email # Використовуйте клас Email
Новий виклик виникає!
Тепер, коли ми змінили клас Person для використання класів PhoneNumber та Email, нам також потрібно змінити спосіб створення екземпляра (створення) Person. Тепер нам спочатку потрібно створити об’єкт PhoneNumber та Email, перш ніж ми зможемо створити Person.
bob_kaare = Person(name="Bob Kåre",
eye_color="blue",
phone_number=PhoneNumber("12345678"), # Зверніть увагу на зміну тут
email=Email("bob_kaare@example.com")) # Зверніть увагу на зміну тут
print(bob_kaare)
# Увага: також потрібна зміна в способі отримання значень
print(bob_kaare.email.address)
print(bob_kaare.phone_number.number) # .country_code(?)
Завдання 5 - Властивості в класах (Необов’язково)
Коли ми використовуємо об’єкти для представлення значень, таких як електронна пошта та номери телефонів, нам потрібно вказувати підзначення (наприклад, address для електронної пошти та number для номера телефону) кожного разу, коли ми хочемо отримати значення. Це може стати трохи громіздким у довгостроковій перспективі. На щастя, є рішення цієї проблеми, яке полягає у використанні декоратора @property в класі, що дозволяє нам отримувати значення безпосередньо з об’єкта, не вказуючи підзначення.
Це, однак, створює ще одну проблему, а саме те, що нам потрібна функція __init__ в класі Person. Це тому, що ми не можемо використовувати одне й те саме ім’я як для властивості, так і для атрибута в dataclass.
from dataclasses import dataclass
@dataclass
class EksempelVerdi:
attributt: str
@dataclass
class Person:
name: str
_verdi: EksempelVerdi # Внутрішня змінна (починається з _, щоб вказати на те, що вона "приватна")
def __init__(self, name: str, verdi: EksempelVerdi):
self.name = name
self._verdi = verdi
@property
def verdi(self):
return self._verdi.attributt # Отримує підзначення напряму
# Тест коду
person = Person(name="Alice", verdi=EksempelVerdi("Noe tekst"))
print(person.verdi) # Вихідні дані: Noe tekst
Примітка
Властивості є унікальними тим, що їм не потрібні параметри і їх не потрібно викликати з дужками. У прикладі ми отримуємо значення person.value без дужок (а не person.value(), хоча технічно це функція).
Альтернативний приклад, який приймає як str, так і ExampleValue
@dataclass
class Person:
name: str
_value: str
def __init__(self, name: str, value: str | ExampleValue):
self.name = name
if isinstance(value, ExampleValue):
self._value = value
elif isinstance(value, str):
self._value = ExampleValue(value)
else:
raise TypeError("value має бути типу str або ExampleValue")
@property
def value(self) -> str:
return self._value.attribute # Отримує підзначення безпосередньо
# Тест коду
person = Person(name="Аліса", value="Деякий текст")
print(person.value) # Вихідні дані: Деякий текст
Завдання 6 — Властивості з логікою (Необов’язкове)
Оновте клас Person, додавши новий атрибут на ім’я birthday (день народження). Він має бути типу datetime.date (з бібліотеки datetime).
Потім створіть такі властивості:
- Створіть властивість
age, яка обчислює вік особи на основіbirthdayта поточної дати. - Створіть властивість
is_adult, яка повертаєTrue, якщо особі є 18 років або більше, інакшеFalse.
Рішення: Вік та дорослість як властивості
Ось можливе рішення:
from dataclasses import dataclass
from datetime import date
@dataclass
class Person:
name: str
birthday: date
@property
def age(self) -> int:
"""Обчислює вік на основі дати народження та поточної дати"""
today = date.today()
age = today.year - self.birthday.year
# Зменшує на 1, якщо день народження ще не настав цього року
if (today.month, today.day) < (self.birthday.month, self.birthday.day):
age -= 1
return age
@property
def is_adult(self) -> bool:
"""Повертає True, якщо особі 18 років або більше"""
return self.age >= 18
# Перевірте код
person = Person(name="Alice", birthday=date(2005, 5, 15))
print(person.age) # Наприклад, 18, якщо поточна дата після 15 травня 2023 року
print(person.is_adult) # True
Ще одна задача!
Чи зможете ви налаштувати інстанціювання Person так, щоб воно приймало як datetime.date, так і текст у форматі "DD-MM-YYYY" для birthday? (Підказка: використовуйте datetime.strptime для перетворення рядка на datetime.date)
