diff --git a/mod_oop/2.3_10_validate_string.py b/mod_oop/2.3_10_validate_string.py new file mode 100644 index 0000000..55ca082 --- /dev/null +++ b/mod_oop/2.3_10_validate_string.py @@ -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"
") + for name, field in zip("Логин Пароль Email".split(), self.get_fields()): + print(f"{name}: {field}") + print(f"
") + + +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() diff --git a/mod_oop/2.3_11_supershop.py b/mod_oop/2.3_11_supershop.py new file mode 100644 index 0000000..ec9acef --- /dev/null +++ b/mod_oop/2.3_11_supershop.py @@ -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() diff --git a/mod_oop/2.3_12_bag_of_things.py b/mod_oop/2.3_12_bag_of_things.py new file mode 100644 index 0000000..298f8e1 --- /dev/null +++ b/mod_oop/2.3_12_bag_of_things.py @@ -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() diff --git a/mod_oop/2.3_13_tvprogram.py b/mod_oop/2.3_13_tvprogram.py new file mode 100644 index 0000000..8ac1ca6 --- /dev/null +++ b/mod_oop/2.3_13_tvprogram.py @@ -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()