+ oop tasks
This commit is contained in:
parent
b4c92e894b
commit
402db810bc
100
mod_oop/2.3_10_validate_string.py
Normal file
100
mod_oop/2.3_10_validate_string.py
Normal file
@ -0,0 +1,100 @@
|
||||
# https://stepik.org/lesson/701985/step/10?unit=702086
|
||||
|
||||
|
||||
class ValidateString:
|
||||
def __init__(self, min_length=0, max_length=1000):
|
||||
self.min_length, self.max_length = min_length, max_length
|
||||
|
||||
def validate(self, value):
|
||||
return (
|
||||
isinstance(value, str) and self.min_length <= len(value) <= self.max_length
|
||||
)
|
||||
|
||||
|
||||
class StringValue:
|
||||
def __init__(self, validator=None):
|
||||
if validator is None:
|
||||
validator = ValidateString(min_length=3, max_length=100)
|
||||
self.validator = validator
|
||||
|
||||
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 RegisterForm:
|
||||
login = StringValue()
|
||||
password = StringValue(ValidateString(min_length=3, max_length=100))
|
||||
email = StringValue()
|
||||
|
||||
def __init__(self, login, password, email):
|
||||
self.login, self.password, self.email = login, password, email
|
||||
|
||||
def get_fields(self):
|
||||
return [self.login, self.password, self.email]
|
||||
|
||||
def show(self):
|
||||
print(f"<form>")
|
||||
for name, field in zip("Логин Пароль Email".split(), self.get_fields()):
|
||||
print(f"{name}: {field}")
|
||||
print(f"</form>")
|
||||
|
||||
|
||||
def tests():
|
||||
assert hasattr(
|
||||
ValidateString, "validate"
|
||||
), "в классе ValidateString отсутствует метод validate"
|
||||
|
||||
r = RegisterForm("11111", "1111111", "11111111")
|
||||
assert (
|
||||
hasattr(r, "login") and hasattr(r, "password") and hasattr(r, "email")
|
||||
), "в классе RegisterForm должны быть дескрипторы login, password, email"
|
||||
|
||||
assert hasattr(RegisterForm, "show"), "в классе RegisterForm отсутствует метод show"
|
||||
|
||||
StringValue.__doc__
|
||||
|
||||
frm = RegisterForm("123", "2345", "sc_lib@list.ru")
|
||||
assert frm.get_fields() == [
|
||||
"123",
|
||||
"2345",
|
||||
"sc_lib@list.ru",
|
||||
], "метод get_fields вернул неверные данные"
|
||||
|
||||
frm.login = "root"
|
||||
assert frm.login == "root", "дескриптор login вернул неверные данные"
|
||||
|
||||
v = ValidateString(5, 10)
|
||||
assert v.validate("hello"), "метод validate вернул неверное значение"
|
||||
assert v.validate("hell") == False, "метод validate вернул неверное значение"
|
||||
assert (
|
||||
v.validate("hello world!") == False
|
||||
), "метод validate вернул неверное значение"
|
||||
|
||||
class A:
|
||||
st = StringValue(validator=ValidateString(3, 10))
|
||||
|
||||
a = A()
|
||||
a.st = "hello"
|
||||
assert a.st == "hello", "дескриптор StringValue вернул неверное значение"
|
||||
a.st = "d"
|
||||
assert (
|
||||
a.st == "hello"
|
||||
), "дескриптор StringValue сохранил строку длиной меньше min_length"
|
||||
a.st = "dапарпаропропропропр"
|
||||
assert (
|
||||
a.st == "hello"
|
||||
), "дескриптор StringValue сохранил строку длиной больше max_length"
|
||||
a.st = "dапарпароп"
|
||||
assert (
|
||||
a.st == "dапарпароп"
|
||||
), "дескриптор StringValue сохранил строку длиной больше max_length"
|
||||
|
||||
|
||||
tests()
|
108
mod_oop/2.3_11_supershop.py
Normal file
108
mod_oop/2.3_11_supershop.py
Normal file
@ -0,0 +1,108 @@
|
||||
# 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()
|
59
mod_oop/2.3_12_bag_of_things.py
Normal file
59
mod_oop/2.3_12_bag_of_things.py
Normal file
@ -0,0 +1,59 @@
|
||||
# https://stepik.org/lesson/701985/step/12?unit=702086
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
class Thing:
|
||||
def __init__(self, name: str, weight: int):
|
||||
self.name, self.weight = name, weight
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self.name!r}, {self.weight!r})"
|
||||
|
||||
|
||||
class Bag:
|
||||
def __init__(self, max_weight: int):
|
||||
self.__things: List[Thing] = []
|
||||
self.max_weight = max_weight
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self.max_weight!r})"
|
||||
|
||||
@property
|
||||
def weight(self) -> int:
|
||||
return sum(t.weight for t in self.__things)
|
||||
|
||||
@property
|
||||
def remaining_weight(self) -> int:
|
||||
return self.max_weight - self.weight
|
||||
|
||||
@property
|
||||
def things(self) -> List[Thing]:
|
||||
return self.__things
|
||||
|
||||
def add_thing(self, thing: Thing):
|
||||
if self.remaining_weight >= thing.weight:
|
||||
self.__things.append(thing)
|
||||
|
||||
def remove_thing(self, indx: int):
|
||||
del self.__things[indx]
|
||||
|
||||
def get_total_weight(self) -> int:
|
||||
return self.weight
|
||||
|
||||
|
||||
bag = Bag(1000)
|
||||
bag.add_thing(Thing("Книга по Python", 100))
|
||||
bag.add_thing(Thing("Котелок", 500))
|
||||
bag.add_thing(Thing("Спички", 20))
|
||||
bag.add_thing(Thing("Бумага", 100))
|
||||
w = bag.get_total_weight()
|
||||
for t in bag.things:
|
||||
print(f"{t.name}: {t.weight}")
|
||||
|
||||
|
||||
def tests():
|
||||
...
|
||||
|
||||
|
||||
tests()
|
148
mod_oop/2.3_13_tvprogram.py
Normal file
148
mod_oop/2.3_13_tvprogram.py
Normal file
@ -0,0 +1,148 @@
|
||||
"""
|
||||
https://stepik.org/lesson/701985/step/13?unit=702086
|
||||
|
||||
Необходимо написать программу для представления и управления расписанием телевизионного вещания. Для этого нужно объявить класс TVProgram, объекты которого создаются командой:
|
||||
|
||||
>>> repr(TVProgram("название канала"))
|
||||
"TVProgram('название канала')"
|
||||
|
||||
где название канала - это строка с названием телеканала.
|
||||
|
||||
В каждом объекте класса TVProgram должен формироваться локальный атрибут:
|
||||
items - список из телепередач (изначально список пуст).
|
||||
>>> TVProgram("название канала").items
|
||||
[]
|
||||
|
||||
В самом классе TVProgram должны быть реализованы следующие методы:
|
||||
add_telecast(self, tl) - добавление новой телепередачи в список items;
|
||||
remove_telecast(self, indx) - удаление телепередачи по ее порядковому номеру (атрибуту __id, см. далее).
|
||||
>>> {hasattr(TVProgram, m) for m in "add_telecast remove_telecast".split()}
|
||||
{True}
|
||||
|
||||
Каждая телепередача должна описываться классом Telecast, объекты которого создаются командой:
|
||||
|
||||
>>> repr(Telecast(1, "название", 1111))
|
||||
"Telecast(1, 'название', 1111)"
|
||||
|
||||
где 1 - порядковый номер - номер телепередачи в сетке вещания (от 1 и далее); "название" - наименование телепередачи; 1111 - длительность - время телепередачи (в секундах - целое число).
|
||||
|
||||
Соответственно, в каждом объекте класса Telecast должны формироваться локальные приватные атрибуты:
|
||||
|
||||
__id - порядковый номер (целое число);
|
||||
__name - наименование телепередачи (строка);
|
||||
__duration - длительность телепередачи в секундах (целое число).
|
||||
>>> {hasattr(obj, a) for obj in (Telecast(1, "название", 1111),) for a in "__id __name __duration".split()}
|
||||
{True}
|
||||
|
||||
Для работы с этими приватными атрибутами в классе Telecast должны быть объявлены соответствующие объекты-свойства (property):
|
||||
|
||||
uid - для записи и считывания из локального атрибута __id;
|
||||
name - для записи и считывания из локального атрибута __name;
|
||||
duration - для записи и считывания из локального атрибута __duration.
|
||||
>>> {hasattr(obj, a) for obj in (Telecast(1, "название", 1111),) for a in "uid name duration".split()}
|
||||
{True}
|
||||
|
||||
Пример использования классов (эти строчки в программе писать не нужно):
|
||||
|
||||
>>> def test():
|
||||
... pr = TVProgram("Первый канал")
|
||||
... pr.add_telecast(Telecast(1, "Доброе утро", 10000))
|
||||
... pr.add_telecast(Telecast(2, "Новости", 2000))
|
||||
... pr.add_telecast(Telecast(3, "Интервью с Балакиревым", 20))
|
||||
... return [*map(lambda t: f"{t.name}: {t.duration}", pr.items)]
|
||||
>>> test()
|
||||
['Доброе утро: 10000', 'Новости: 2000', 'Интервью с Балакиревым: 20']
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
def make_properties(names: List[str]):
|
||||
def decorator(cls):
|
||||
def prop(private_name: str):
|
||||
def getter(self):
|
||||
return getattr(self, private_name)
|
||||
|
||||
def setter(self, value):
|
||||
return setattr(self, private_name, value)
|
||||
|
||||
return getter, setter
|
||||
|
||||
for name in names:
|
||||
private_name = "__id" if name == "uid" else f"__{name}"
|
||||
setattr(cls, name, property(*prop(private_name)))
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@make_properties("uid name duration".split())
|
||||
class Telecast:
|
||||
def __init__(self, uid: int, name: str, duration: int):
|
||||
self.uid, self.name, self.duration = uid, name, duration
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}{(self.uid, self.name, self.duration)!r}"
|
||||
|
||||
|
||||
class TVProgram:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.items: List[Telecast] = []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}('{self.name}')"
|
||||
|
||||
def add_telecast(self, telecast: Telecast):
|
||||
self.items.append(telecast)
|
||||
|
||||
def remove_telecast(self, indx: int):
|
||||
self.items = [item for item in self.items if item.uid != indx]
|
||||
|
||||
|
||||
def tests():
|
||||
assert hasattr(TVProgram, "add_telecast") and hasattr(
|
||||
TVProgram, "remove_telecast"
|
||||
), "в классе TVProgram должны быть методы add_telecast и remove_telecast"
|
||||
|
||||
pr = TVProgram("Первый канал")
|
||||
pr.add_telecast(Telecast(1, "Доброе утро", 10000))
|
||||
pr.add_telecast(Telecast(3, "Новости", 2000))
|
||||
t = Telecast(2, "Интервью с Балакиревым", 20)
|
||||
pr.add_telecast(t)
|
||||
|
||||
pr.remove_telecast(3)
|
||||
assert (
|
||||
len(pr.items) == 2
|
||||
), "неверное число телеперач, возможно, некорректно работает метод remove_telecast"
|
||||
assert (
|
||||
pr.items[-1] == t
|
||||
), "удалена неверная телепередача (возможно, вы удаляете не по __id, а по порядковому индексу в списке items)"
|
||||
|
||||
assert (
|
||||
type(Telecast.uid) == property
|
||||
and type(Telecast.name) == property
|
||||
and type(Telecast.duration) == property
|
||||
), "в классе Telecast должны быть объекты-свойства uid, name и duration"
|
||||
|
||||
for x in pr.items:
|
||||
assert hasattr(x, "uid") and hasattr(x, "name") and hasattr(x, "duration")
|
||||
|
||||
assert (
|
||||
pr.items[0].name == "Доброе утро"
|
||||
), "объект-свойство name вернуло неверное значение"
|
||||
assert (
|
||||
pr.items[0].duration == 10000
|
||||
), "объект-свойство duration вернуло неверное значение"
|
||||
|
||||
t = Telecast(1, "Доброе утро", 10000)
|
||||
t.uid = 2
|
||||
t.name = "hello"
|
||||
t.duration = 10
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
doctest.testmod()
|
||||
tests()
|
Loading…
Reference in New Issue
Block a user