109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
# https://stepik.org/lesson/701985/step/11?unit=702086
|
||
|
||
from typing import Protocol
|
||
|
||
|
||
class Validator(Protocol):
|
||
def validate(self, value: str) -> bool:
|
||
...
|
||
|
||
|
||
class ValidatedField:
|
||
ValueValidator: Validator
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.validator = self.ValueValidator(*args, **kwargs)
|
||
|
||
def __set_name__(self, owner, name):
|
||
self.name = "_" + name
|
||
|
||
def __get__(self, instance, owner):
|
||
return getattr(instance, self.name)
|
||
|
||
def __set__(self, instance, value):
|
||
if self.validator.validate(value):
|
||
setattr(instance, self.name, value)
|
||
|
||
|
||
class StringValue(ValidatedField):
|
||
class ValueValidator(Validator):
|
||
def __init__(self, min_length, max_length):
|
||
self.min_length, self.max_length = min_length, max_length
|
||
|
||
def validate(self, value: str) -> bool:
|
||
return (
|
||
isinstance(value, str)
|
||
and self.min_length <= len(value) <= self.max_length
|
||
)
|
||
|
||
|
||
class PriceValue(ValidatedField):
|
||
class ValueValidator(Validator):
|
||
def __init__(self, max_value):
|
||
self.max_value = max_value
|
||
|
||
def validate(self, value: str) -> bool:
|
||
return isinstance(value, int) and 0 <= value <= self.max_value
|
||
|
||
|
||
class Product:
|
||
name = StringValue(min_length=2, max_length=50)
|
||
price = PriceValue(max_value=10000)
|
||
|
||
def __init__(self, name, price):
|
||
self.name = name
|
||
self.price = price
|
||
|
||
|
||
class SuperShop:
|
||
def __init__(self, name):
|
||
self.name = name
|
||
self.goods: list[Product] = []
|
||
|
||
def add_product(self, product: Product):
|
||
self.goods.append(product)
|
||
|
||
def remove_product(self, product: Product):
|
||
self.goods.remove(product)
|
||
|
||
|
||
shop = SuperShop("У Балакирева")
|
||
shop.add_product(Product("Курс по Python", 0))
|
||
shop.add_product(Product("Курс по Python ООП", 2000))
|
||
for p in shop.goods:
|
||
print(f"{p.name}: {p.price}")
|
||
|
||
|
||
def tests():
|
||
shop = SuperShop("У Балакирева")
|
||
shop.add_product(Product("name", 100))
|
||
shop.add_product(Product("name", 100))
|
||
assert (
|
||
shop.name == "У Балакирева"
|
||
), "атрибут name объекта класса SuperShop содержит некорректное значение"
|
||
|
||
for p in shop.goods:
|
||
assert p.price == 100, "дескриптор price вернул неверное значение"
|
||
assert p.name == "name", "дескриптор name вернул неверное значение"
|
||
|
||
t = Product("name 123", 1000)
|
||
shop.add_product(t)
|
||
shop.remove_product(t)
|
||
assert (
|
||
len(shop.goods) == 2
|
||
), "неверное количество товаров: возможно некорректно работают методы add_product и remove_product"
|
||
|
||
assert hasattr(shop.goods[0], "name") and hasattr(shop.goods[0], "price")
|
||
|
||
t = Product(1000, "name 123")
|
||
if hasattr(t, "_name"):
|
||
assert type(t.name) == str, "типы поля name должнен быть str"
|
||
if hasattr(t, "_price"):
|
||
assert type(t.price) in (
|
||
int,
|
||
float,
|
||
), "тип поля price должнен быть int или float"
|
||
|
||
|
||
tests()
|