Спасибо огромное! Вы - талантливый учитель. Все по полочкам с акцентами на причины действий. Так материал воспринимается и усваивается, как вполне простой. Другие учителя больше "колдуют", пытаясь вызвать удивление. Удивляться не хочу, хочу учиться!
Урок #10 = Наконец-то пройден Засел на этом уроке, около часа. 2 раза пересматривал, чтобы точно все понять, и параллельно практиковался. Для себя понял, что все это время, очень сильно усложнял проверки, и в целом код, в своих псевдо проектах. Уже как-то начинается меняться логика с обычного знатока, который просто знает синтаксис, на реально похожего на разработчика. Спасибо Вам. Хоть и понимаю, что это даже не верхушка айсберга, но все же, начало положено :)
Вы очень понятно и доступно объясняете! Я уже просмотрела ролики по ООП у другого автора на ютуб, при чем не бесплатно, и не всегда было понятно для чего оно и как использовать. Ваши видео не просто освежают знания, но и расставляют по полочкам то что недопоняла раньше. СПАСИБО ЗА ВАШ ТРУД!
Маленькое уточнение к тексту в районе 6-7 минут: список f получен с помощью метода split, поэтому он не может содержать пустые строки. Даже если попадутся два подряд пробела, они будут восприняты как единый разделитель. Таким образом, проверка на нулевую длину строки s явно излишня.
тоже это заметил, как ни вводи ФИО его придется либо ввести(чтобы не было ошибки), а если не ввести то возникнет ошибка из-за того что не указан аргумент, я правильно понял ?
Сергей, всё очень здорово. Писал раньше только линейно в ООП перехожу вместе с вами. несколько незначительных ремарок(для новичков в программировании они могут быть полезны): 1. Возраст на английском "age" (old - это прилагательное "старый"... мне на протяжении ролика очень сильно резало слух) 2. C точки зрения хранения таких данных, корректнее хранить дату рождения, а возраст рассчитывать от ДР 3. Если будет введен вес целым числом - это же не ошибка... ;)
@@Работа-н9в По сути вариант с "How old are you?" - Подразумевает "Насколько ты стар(а)" - Просто прижилась такая формулировка. Но ещё используют и вариант по типу: "What's your age?" но гораздо реже первого
Отлично показаны best practices , да еще на пальцах. Спасибо! Сергей, понимаю, что это примеры обучающие примитивные. Но можно предлагать альтернативные варианты проверок для самостоятельного обучения. Например проверку допустимых символов через множества. Форма серии номера паспорта через регулярные выражения. Реализовывать не надо, просто пометку ....тут можно по другому, это задание на 5+, кто чувствует силы попытайтесь сами, нет - пропустите.
Самое сложно было понять, почему в конце убрали __ перед свойствами. self.item внутри __init__ - это имя нашего объекта property. То есть мы просто вынесли присваивание self.item = __item в сеттер.
Я правильно понял что скажем с примером old. self.old = old эта команда вызывает сеттер @old.setter в котом и проходит проверка? И это всё происходит потому что у сеттера приоритет выше?
@@ymnop9652 я тоже полез в комменты за поиском ответа. Скорее всего вы правы, дело в высшем приоритете сеттера, а значит переопределить значение обратившись напрямую к свойству не получится, только через сеттер
Спасибо большое за этот урок! Все понятно, особенно понравился последний штрих. Мне показалось это изящным) Но при этом же, возникло пару вопросов - а удобно ли это, когда у тебя все приватные переменные класса раскиданы по куче функций? Не возникает ли путаница, приватный метод задан в __init__ или нет? Ведь условное self.old = old выглядит как обычное, неприватное свойство, пока не изучишь код этого класса. Или это неважно? Типа так и рассчитано, чтобы чужой разработчик не лез куда не надо)) Вот у тебя свойство, им и пользуйся. Надеюсь, мои вопросы не сильно глупые.
Доброго времени суток. Во время просмотра видео у меня возник один вопрос, объясните пожалуйста, кто знает. Для чего мы делаем свойства приватными, а затем с помощью property даем доступ к этим свойствам пользователям? Почему нельзя просто взять и изначально сделать свойство old, общедоступным? То есть по факту мы с помощью property "отменили" приватность свойства old, так как теперь мы можем и записывать и извлекать значение.
Сергей, спасибо большой за практический пример! Единственно, мне кажется, что все эти валидации можно было бы в несколько раз короче написать через регулярные выражения)) Или тут смысл в том, чтобы более гибко классифицировать ошибки?
Когда мы создавали сеттеры и геттеры (идентичные по наполнению и различные только именем переменной), возникла мысль, что можно создать некую функцию, которая будет автоматически создавать в моменте сеттер и геттер по использованному при вызове аргументу (чтобы не дублировать для каждого аргумента сеттер и геттер) такое действительно можно реализовать?
def radius_property(name) -> property: def r_setter(self, value) -> None: if isinstance(value, int): if value < 0: raise ValueError(f"Радиус должен быть неотрицательным, но задано значение: {value}") else: if "_Figure" + name in self.__dict__: self.__dict__["_Figure" + name] = value else: raise ValueError(f"Не существует свойства с именем: {name}") else: raise TypeError(f"Радиус должен быть типа int, но задан: {type(value)}") def r_getter(self) -> int: if "_Figure" + name in self.__dict__: return self.__dict__["_Figure" + name] else: raise ValueError(f"Не существует свойства с именем: {name}") return property(r_getter, r_setter) # Делаем сколько угодно свойств с общими сеттерами и геттерами для обработки радиусов r1 = radius_property("__inner_radius") r2 = radius_property("__outer_radius") r3 = radius_property("__base_radius") # Основной код k = Figure() print(k.radius_sum()) k.r1 = 9 k.r2 = 11 print(k.radius_sum()) print(k.r3)
в самом конце немного не понятно, почему когда удаляли __ у аргументов, это сразу значило что будет выполнятся проверка аргументов на условия в сеттерах?
Отличный плейлист! Спасибо большое за уроки. Я не очень понимаю, почему всё эти проверочные методы это классовые методы. В моём понимании их функционал не задействует ни классовые атрибуты, ни атрибуты экземпляров класса, то есть имеет смысл сделать их статическими. Так ли это или я чего-то не понимаю?
Спасибо! Здесь учебный пример для демонстрации, чтобы вы знали возможности языка. Всегда делайте обычные методы, а если возникнет необходимость, то уже задумайтесь над другими типами.
Добрый день. Не могу разобраться как кратко и четко описать property. Правильно ли будет сказать, что главной опцией объектов property является создание общего имени для сеттера, геттера или делитера, которое хранит ссылки на них и через это имя, в зависимости от используемого оператора (присвоения, удаления, отсутствия оператора) можно вызвать нужный метод?
@@selfedu_rus а еще вопрос. Курс на степике не планируете делать? А то вроде всё понятно, но хотелось бы закрепить. В интернете искал задачи, но пока что без успешно)
Вы переодически используете метод type(). Читая книгу по Python, автор показывал этот метод, но всегда говорил, что лучше не использовать его, так как тем самым мы ограничиваем принципы динамической типизации. Как считаете, прав ли автор, не считается ли это плохим тоном программирования на Python?
Если брать isinstance(), то например, для типа bool будет проходить и тип int, так как bool наследуется от него. А вот type() отработает четко. Все зависит от задачи.
Здравствуйте, а случаем ли не избыточная проверка на len(s) < 1? Просто при split уже отсекаются строки с нулевым значением. Если нет, то приведите пример
лучше сделать для надежности последующего редактирования программы; если на все полагаться, то малейшие изменения в коде приведут к большим непредвиденным ошибкам
Такое имеет место быть конкретно в этом примере, в случае сплита по вайтспейсу без параметра. На практике, очень часто, например, при парсинге CSV или табулированных таблиц, разделители уже конкретные, и пустые строки возникают сплошь и рядом. Поэтому, в случае обработки табличного вида данных, проверки на пустые строки не то что не лишние, а просто необходимы, в силу того, что обычный сплит без параметра там как правило не применяется. Ну, а если случай тривиальный, и точно уверен, что разделителем всегда будет пробельная последовательность - то да, тут пустых строк не может быть.
@@SayXaNow а ну кстати да, почему-то сразу не подумал про то что сплитовать придется не только по пробелам, так программа более универсальная получается, спасибо!
@@PythonSthenics Пересмотрел и действительно - есть. Извиняюсь, был не прав. Видимо это эффект от обучения - смотрю как баран на новые ворота, в упор многое не замечаю.
В волшебных методов __setatr, __getatr, __getatribute тоже можна писать проверки и так же использывать как Вы в этом уроке. Я понимаю, что можна писать по-разному, но эти волшебные методы тогда более пригодятся в более сложных примерах или можна так и так как нравится писать? Хочу научится понимать тонкости. Спасибо
Здравствуйте! Хотел уточнить, в плане структуры не лучше было создать дочерний класс с валидацией этих данных и поместить его в другой файл?(А в основной - этот, импортировать)
Если валидация привязана исключительно к данным класса Person, то городить огород из иерархий не вижу смысла. Если у вас планируется некий универсальный проверочный класс, то можно реализовать его как миксин.
Спасибо за видео. Скажите, а свойства класса S_RUS и S_RUS_UPPER не лучше сделать приватными и обращаться к ним через соответствующий геттер? Заранее спасибо.
я не могу понять, зачем вы проверяете наличие минимум 1 символа, хотя перед этим проверяли наличие 3 элементов, ведь если будет меньше 1 символа, то наверное будет меньше 3 элементов, разве нет?
@@selfedu_rus это я понял. Но мы же их делали для инкапсуляции. То есть чтобы программист, который будет работать с нашим классом знал, что данное поле должно быть использовано внутри класса и работать с ним извне не надо(на сколько я понял) . Но теперь с помощью property мы делаем так чтобы могли обращаться к этим полям извне. Пока писал появилось предположение. Это для того чтобы мы могли читать и записывать их каким то особым образом через методы для добавления и чтения?
Я запутался, помогите! В предыдущем уроке сказано, что @property применяется для того, чтобы не плодить def get_ и def set_ для каждого атрибута. Но в этом видео мы так-же создаем геттер и сеттер для каждого атрибута, но еще и с доп строкой в виде @property или @.setter. Смысл?
Не совсем верно. Property - это особый тип (класс) данных. Переменные этого типа могут использоваться в выражениях так же, как и привычные типы данных (str, int) и т.д. Что более интуитивно и понятно. Если radius - наше свойство типа Property, то мы легко можем писать выражения вида: a.radius = 100 / (k + b + c) length = 8 * math.pi * a.radius Вместо: a.set_radius(100 / (k + b + c)) length = 8 * math.pi * a.get_radius() ключевой особенностью этого типа данных является то, что он имеет свой собственный настраиваемый сеттер и геттер, который будет вызван при обращении к переменной этого типа. Что позволяет помимо банального извлечения или присвоения значения, прикрутить какую угодно дополнительную логику в сеттер и геттер. Например, сеттер автоматически может проверять корректность данных, и выдавать ошибку, если присваивается отрицательное значение. PS и да, в питоне на самом деле нет привычных типов данных как в других языках программирования. str, int и т.д. - это объекты (классы), через которые и происходит «симулирование» типов данных. Все в питоне - это объекты, функции - тоже объекты. Когда осознаешь это, станут понятны многие вещи, над которыми раньше не задумывался. s = "hello world" - создание класса str и присвоение ему значения "hello world" s.partition(" ") - вызов метода класса s (4 + 7).bit_length() - создание класса int, присвоение ему значения 11 и вызов метода класса bit_length(), для получения длины значения в битах. --------------------- Создание собственного класса, который будет обладать всеми свойствами строкового типа данных и дополнительным методом транслитерации: class my_string(str): def transliterate(self): d = {"р": "r", "в": "v"} return "".join([d[x] if x in d else x for x in self.__str__()]) s = my_string("Привет, мир!") print(s.count("и")) - унаследовано от базового класса str print(s.transliterate()) - применение нашего собственного метода.
Назрел вопрос. Если я буду пользоваться не @classmethod а обычным методом, но в параметре укажу cls - эту будет считаться плохим тоном или из этого в будущем можно получить ошибку. В данной программе все работает без ошибок.
@@selfedu_rus Классно, что есть обратная связь. Попытался доработать форму, чтобы можно было вводить данные не в коде а в консоли. Прописал строку g = Person(input(), input(), input(), input()), но программа пропускает числовые инпуты. Пробовал обернуть числовые int() и float(), выдает ошибку ValueError: invalid literal for int() with base 10: '' Подскажите что не так?
чтобы не прописывать все символы которые мы хотим проверить можно еще так прописать проверку через регулярные выражения import re if re.search(r'[^А-Яа-яЁё\s\-]', fio) != None: raise TypeError('Допустимы только Буквы, тире и пробел!')
Так же подумал и сделал, но вот вопрос, класс же теперь за собой тянет Re модуль, и нельзя им воспользоваться его нţимпортируя. Как вы думаете, имеет ли это значение?
@@RealMineManUK , ну модуль string Сергей импортирует без зазрений совести, так что не в том дело. re - тоже стандартная библииотека. Сергей писал, что regular expressions не использует, чтоб отдельно не объяснять, что это такое тем, кто не в курсе.
@@selfedu_rus та не, я может неправильно написал. Напишите мне условие при котором мы вводим ФИО и выдает ошибку " в ФИО должен быть хотя бы один символ", пожалуйста, а то все равно не понимаю целесобразность этой проверки. Как по мне, она взаимоисключается при проверки на длину ФИО из 3 слов и что все символы буквы. СПАСИБО ВАМ, ЗА ВАШ ТРУД! Я хоть и не все понимаю, для меня не хватает домашних заданий....прям очень сильно!
Можно использовать в classmetod вот так(если Python 3.10): @classmethod def verify_weight(cls, weight: int | float): if not isinstance(weight, int | float) or weight < 20: raise TypeError(".......") извините за мой руский, если есть ошибки.
А зачем метод валидации возраста, веса и паспорта декорированы как classmethod, он же с данными класса не работают. И их поидее лучше staticmthod декорировать. И валидация ФИО тоже под вопросом. Данные класса же не изменяются, а только берутся для проверки символов, почему его простым методом не оставить. Мб я что-то не понимаю?
А для нужен был декоратор @classmethod? Вы же говорили, что @classmethod используется в основном для статических методов. Ещё хотел сказать из опыта, что отчество всегда должно быть необязательным полем, так как у некоторых народов отчество является частью имени (поэтому отчество у них отсутствует), например, у монголов, тюрков и т. д.
Через @classmethod мы описываем методы, которые работают только с атрибутами класса, а не его экземпляров. С отчеством да, согласен, ну это учебный пример )
Не совсем понятно применение в данном примере методов класса (за исключением verify_fio, т.к. в этом методе есть обращение к атрибутам класса) Почему именно методы класса, а не статические методы?
строгих правил тут нет, но если мы в методе не собираемся работать с атрибутами объектов, то это кандидат на класс-метод. Статические вообще очень редко задаются и, как правило, это какие то сторонние вспомогательные функции, связанные как то с тематикой класса и не более того. С опытом придет понимание.
@@selfedu_rus спасибо за ответ! Пытаюсь понять когда что используется. Считал, что если не работаем ни с атрибутами класса, ни с атрибутами экземпляра класса, то это и есть безальтернативный вариант статического метода.
Созрел вопрос после просмотра лекций - Как сделать такой же единый интерфейс(сеттер, геттер) но чтоб сеттер мог принимать не одно значение а несколько? Я пробовал с помощью проперти но как я понял там можно передавать только одно значение.. Ну или если я что-то упустил то поправьте меня :)
Пока досконально не проверял, но на первый взгляд в проверке ФИО есть загвоздка с пробелами. По логике они должны быть раз используется split без аргументов, но в verify_fio они не разрешены.
Это такая своя традиция когда пишешь на питоне, называть переменные одной буквой, чтоб через пять минут не помнить, что это? Не у первого автора это вижу, глаза режет и душу рвёт.
@@Majohne можно показывать как делать не правильно, но не предупреждая, что это не правильно? По этой логике потом люди так же код и пишут, как увидели.
@@nikolaydemchenko6741 вряд-ли человек будет изучать ООП, если не изучил базу языка, в уроках которой обычно это все объясняют. Хотя мб есть и такие, кто при объяснении самого языка это используют и не говорят об этом
@@Majohne если ты пишешь код, и много, ты не будешь страдать такой ерундой типа тут и одной буквой сойдёт. У тебя в голове такое не возникнет в принципе, ты просто назовешь переменную максимально понятно и не будешь об этом вообще думать. И в этом курсе не совсем про ООП. Тут про реализацию классов и объектов в данном языке. Хотя многие думают, что это уже ООП.
Все зависит от задачи. Здесь да, можно isintance, но вот для проверки булевых величин isintance может возвращать неверный результат, т.к. выполняет определение типа с учетом наследования. Функция type более строгая.
Всё чудесно и понятно, НО у меня почему то выскакивает ошибка на строке 12 self.verify_weight(weight) после запуска пишет line 12, in __init__ self.verify_weight(weight) AttributeError: 'Person' object has no attribute 'verify_weight'. Did you mean: 'verify_weght'? вот выложу весь код: from string import ascii_letters class Person: S_RUS = 'абвгдеёжзийклмнопрстуфхцчшщьыъэюя-' S_RUS_UPPER = S_RUS.upper() def __init__(self, fio, old, ps, weight): self.verify_fio(fio) self.verify_old(old) self.verify_ps(ps) self.verify_weight(weight) self.__fio = fio.split() self.__old = old self.__ps = ps self.__weight = weight @classmethod def verify_fio(cls, fio): if type(fio) != str: raise TypeError("ФИО должно быть строкой") f = fio.split() if len(f) != 3: raise ValueError("Неверный формат ФИО") letters = ascii_letters + cls.S_RUS + cls.S_RUS_UPPER for s in f: if len(s) < 1: raise TypeError("В ФИО должен быть хотя бы один символ") if len(s.strip(letters)) != 0: raise TypeError("В ФИО можно использовать только буквенные символы и дефис") @classmethod def verify_old(cls, old): if type(old) != int or old < 14 or old > 120: raise TypeError("Возраст должен быть целым числом в диапазоне [14, 120]") @classmethod def verify_weght(cls, w): if type(w) != float or w < 20: raise TypeError("Вес должен быть вещественным числом от 20 и выше") @classmethod def verify_ps(cls, ps): if type(ps) != str: raise TypeError("Паспорт должен быть строкой") s = ps.split() if len(s) != 2 or len(s[0]) != 4 or len(s[1]) != 6: raise TypeError("Неверный формат паспорта") for p in s: if not p.isdigit(): raise TypeError("Серия и номер паспорта должны быть числами") @property def fio(self): return self.__fio @property def old(self): return self.__old @old.setter def old(self, old): self.verify_old(old) self.__old = old @property def weight(self): return self.__weight @weight.setter def weight(self, weight): self.__weight = weight @property def passport(self): return self.__passport @passport.setter def passport(self, ps): self.verify_ps(ps) self.__passport = ps p = Person('Балакирев Сергей Михайлович', 30, '1234 567890', 80.0) p.old = 100 p.password = '4567 123456' p.weight = 70.0 print(p.__dict__)
Браво!!! Один из лучших курсов по ооп,лаконично,практично,без воды!
Спасибо огромное! Вы - талантливый учитель. Все по полочкам с акцентами на причины действий. Так материал воспринимается и усваивается, как вполне простой. Другие учителя больше "колдуют", пытаясь вызвать удивление. Удивляться не хочу, хочу учиться!
Урок #10 = Наконец-то пройден
Засел на этом уроке, около часа. 2 раза пересматривал, чтобы точно все понять, и параллельно практиковался.
Для себя понял, что все это время, очень сильно усложнял проверки, и в целом код, в своих псевдо проектах.
Уже как-то начинается меняться логика с обычного знатока, который просто знает синтаксис, на реально похожего на разработчика. Спасибо Вам. Хоть и понимаю, что это даже не верхушка айсберга, но все же, начало положено :)
Сергей Михайлович, огромное тебе человеческое 'спасибо'.upper() за понятное донесение знаний!
'спасибо'.upper() - это очень мощно xD
Вы очень понятно и доступно объясняете! Я уже просмотрела ролики по ООП у другого автора на ютуб, при чем не бесплатно, и не всегда было понятно для чего оно и как использовать. Ваши видео не просто освежают знания, но и расставляют по полочкам то что недопоняла раньше. СПАСИБО ЗА ВАШ ТРУД!
нереально крутой пример, понятно все абсолютно хорошо, преподаватель от Бога!
Отдельное спасибо за такой практический урок т к это помогает закрепить материал.
Храни Вас Господь 🙏
Аминь.
класные уроки! их бы еще в курс оформить на степике с практическими заданиями. и будет вообще бомба
Теперь есть, го на степик
@@dmitriivanov7010 уже)
Отличный пример! Спасибо! Понял не только проперти, а ещё классмедоды и вообще начал осознавать смысл ооп :D
Как будто бы начинаю что-то понимать. Спасибо)
wow, Сергей, прям по полочкам всё закидал. благодарю, за лучшие уроки python в рунете
Мне очень нравятся твои видео, на обеде постоянно смотрю
Я тоже смотрела на обеде ❤
Очередное классное видео. Очень все внятно-понятно.
Спасибо за урок! Как всегда по красоте!
Маленькое уточнение к тексту в районе 6-7 минут: список f получен с помощью метода split, поэтому он не может содержать пустые строки. Даже если попадутся два подряд пробела, они будут восприняты как единый разделитель. Таким образом, проверка на нулевую длину строки s явно излишня.
тоже это заметил, как ни вводи ФИО его придется либо ввести(чтобы не было ошибки), а если не ввести то возникнет ошибка из-за того что не указан аргумент, я правильно понял ?
Спасибо за крутой практический урок! Натренировал, пальцы теперь помнят)
Мое почтение за ваши труды!!
Маэстро, отличное объяснение. Отличный пример!
Спасибо. Уже привык к понятным объяснениям.
Как здорово что я Вас нашел :-)
Супер!! Спасибо! Теперь все по полочкам разложилось))))
Я в восторге!!! Огромное спасибо тебе!
давно ждал таких уроков. Спасибо большое
Спасибо, Сергей, за крутой урок!
Спасибо автору, очень подробное изложение материала
Ваши классы просто класс 👍
Сергей, всё очень здорово. Писал раньше только линейно в ООП перехожу вместе с вами.
несколько незначительных ремарок(для новичков в программировании они могут быть полезны):
1. Возраст на английском "age" (old - это прилагательное "старый"... мне на протяжении ролика очень сильно резало слух)
2. C точки зрения хранения таких данных, корректнее хранить дату рождения, а возраст рассчитывать от ДР
3. Если будет введен вес целым числом - это же не ошибка... ;)
Стоп, а почему "How old are you?', когда спрашивают про возраст?
@@Работа-н9в По сути вариант с "How old are you?" - Подразумевает "Насколько ты стар(а)" - Просто прижилась такая формулировка. Но ещё используют и вариант по типу: "What's your age?" но гораздо реже первого
по поводу 3 пункта тоже так подумал и использовал при проверке if not isinstance(weight, (int, float))
Спасибо, посмотрела!
Отличный урок. Спасибо!
Отлично показаны best practices , да еще на пальцах. Спасибо! Сергей, понимаю, что это примеры обучающие примитивные. Но можно предлагать альтернативные варианты проверок для самостоятельного обучения. Например проверку допустимых символов через множества. Форма серии номера паспорта через регулярные выражения. Реализовывать не надо, просто пометку ....тут можно по другому, это задание на 5+, кто чувствует силы попытайтесь сами, нет - пропустите.
лайк и комментарий в поддержку канала
спасибо! живите 100 лет
Спасибо, давайте ещё про классы!
Браво... Прекрасный материал...
Самое сложно было понять, почему в конце убрали __ перед свойствами.
self.item внутри __init__ - это имя нашего объекта property. То есть мы просто вынесли присваивание self.item = __item в сеттер.
Я правильно понял что скажем с примером old. self.old = old эта команда вызывает сеттер @old.setter в котом и проходит проверка? И это всё происходит потому что у сеттера приоритет выше?
@@ymnop9652 я тоже полез в комменты за поиском ответа. Скорее всего вы правы, дело в высшем приоритете сеттера, а значит переопределить значение обратившись напрямую к свойству не получится, только через сеттер
За последний штрих особый лайк-думал скажете или нет))))
Супер, спасибо большое.
четко и по делу!
Здравствуйте, вопрос: а где идёт проверка возраста, веса, паспорта мы же не используем атрибуты класса, не лучше ли использовать staticmethod?
Спасибо большое за этот урок! Все понятно, особенно понравился последний штрих. Мне показалось это изящным)
Но при этом же, возникло пару вопросов - а удобно ли это, когда у тебя все приватные переменные класса раскиданы по куче функций? Не возникает ли путаница, приватный метод задан в __init__ или нет? Ведь условное self.old = old выглядит как обычное, неприватное свойство, пока не изучишь код этого класса. Или это неважно? Типа так и рассчитано, чтобы чужой разработчик не лез куда не надо)) Вот у тебя свойство, им и пользуйся. Надеюсь, мои вопросы не сильно глупые.
Спасибо за урок
Доброго времени суток. Во время просмотра видео у меня возник один вопрос, объясните пожалуйста, кто знает. Для чего мы делаем свойства приватными, а затем с помощью property даем доступ к этим свойствам пользователям? Почему нельзя просто взять и изначально сделать свойство old, общедоступным? То есть по факту мы с помощью property "отменили" приватность свойства old, так как теперь мы можем и записывать и извлекать значение.
Сергей, спасибо большой за практический пример!
Единственно, мне кажется, что все эти валидации можно было бы в несколько раз короче написать через регулярные выражения)) Или тут смысл в том, чтобы более гибко классифицировать ошибки?
Когда мы создавали сеттеры и геттеры (идентичные по наполнению и различные только именем переменной), возникла мысль, что можно создать некую функцию, которая будет автоматически создавать в моменте сеттер и геттер по использованному при вызове аргументу (чтобы не дублировать для каждого аргумента сеттер и геттер) такое действительно можно реализовать?
class Figure:
def __init__(self) -> None:
self.__inner_radius = 1
self.__outer_radius = 1
self.__base_radius = 99
def radius_sum(self) -> int:
return self.__base_radius + self.__inner_radius + self.__outer_radius
def radius_property(name) -> property:
def r_setter(self, value) -> None:
if isinstance(value, int):
if value < 0:
raise ValueError(f"Радиус должен быть неотрицательным, но задано значение: {value}")
else:
if "_Figure" + name in self.__dict__:
self.__dict__["_Figure" + name] = value
else:
raise ValueError(f"Не существует свойства с именем: {name}")
else:
raise TypeError(f"Радиус должен быть типа int, но задан: {type(value)}")
def r_getter(self) -> int:
if "_Figure" + name in self.__dict__:
return self.__dict__["_Figure" + name]
else:
raise ValueError(f"Не существует свойства с именем: {name}")
return property(r_getter, r_setter)
# Делаем сколько угодно свойств с общими сеттерами и геттерами для обработки радиусов
r1 = radius_property("__inner_radius")
r2 = radius_property("__outer_radius")
r3 = radius_property("__base_radius")
# Основной код
k = Figure()
print(k.radius_sum())
k.r1 = 9
k.r2 = 11
print(k.radius_sum())
print(k.r3)
setter и property это как перегрузка оператора = в c++
Спасибо за отличный плейлист! Сколько всего видео планируете снять?
Супер
в самом конце немного не понятно, почему когда удаляли __ у аргументов, это сразу значило что будет выполнятся проверка аргументов на условия в сеттерах?
два подчеркивания - это локальное свойство, а без них - объект property, что мы определили
Спасибо
а почему нельзя использовать обычные функции без декоратора классметод? Мы же просто меняем ссылку на экземпляр на ссылку класса
Отличный плейлист! Спасибо большое за уроки.
Я не очень понимаю, почему всё эти проверочные методы это классовые методы. В моём понимании их функционал не задействует ни классовые атрибуты, ни атрибуты экземпляров класса, то есть имеет смысл сделать их статическими. Так ли это или я чего-то не понимаю?
Спасибо! Здесь учебный пример для демонстрации, чтобы вы знали возможности языка. Всегда делайте обычные методы, а если возникнет необходимость, то уже задумайтесь над другими типами.
в случае с методом verify-fio, надо делать classmethod, т.к. там используются константы класса, а в остальных методах - да, статики
Спасибо за курс.
Есть один вопрос. Почему бы не использовать для проверки паспортных данных regular expressions.
да, вполне можно, просто это отдельный модуль и далеко не все им умеют пользоваться
Добрый день. Не могу разобраться как кратко и четко описать property. Правильно ли будет сказать, что главной опцией объектов property является создание общего имени для сеттера, геттера или делитера, которое хранит ссылки на них и через это имя, в зависимости от используемого оператора (присвоения, удаления, отсутствия оператора) можно вызвать нужный метод?
letters сразу можно было сделать атрибутом класса, тогда он не будет заново пересчитываться при создании нового экземпляра.
12:42 Почему в сеттере не нужно возвращать новое значение? (self.__old = old, почему без return self.__old)?
сеттер только устанавливает параметр, возвращать ничего не требуется
@@selfedu_rus понял, спасибо)
@@selfedu_rus а еще вопрос. Курс на степике не планируете делать? А то вроде всё понятно, но хотелось бы закрепить. В интернете искал задачи, но пока что без успешно)
@@misha8280 просто времени нет, пока не до этого
Great
Святой чел
Вы переодически используете метод type(). Читая книгу по Python, автор показывал этот метод, но всегда говорил, что лучше не использовать его, так как тем самым мы ограничиваем принципы динамической типизации. Как считаете, прав ли автор, не считается ли это плохим тоном программирования на Python?
Если брать isinstance(), то например, для типа bool будет проходить и тип int, так как bool наследуется от него. А вот type() отработает четко. Все зависит от задачи.
Для паспорта и ФИО легче регулярку написать чем сложные методы городить с разбором строк
все в учебных целях
Здравствуйте, а случаем ли не избыточная проверка на len(s) < 1? Просто при split уже отсекаются строки с нулевым значением. Если нет, то приведите пример
лучше сделать для надежности последующего редактирования программы; если на все полагаться, то малейшие изменения в коде приведут к большим непредвиденным ошибкам
Такое имеет место быть конкретно в этом примере, в случае сплита по вайтспейсу без параметра. На практике, очень часто, например, при парсинге CSV или табулированных таблиц, разделители уже конкретные, и пустые строки возникают сплошь и рядом. Поэтому, в случае обработки табличного вида данных, проверки на пустые строки не то что не лишние, а просто необходимы, в силу того, что обычный сплит без параметра там как правило не применяется. Ну, а если случай тривиальный, и точно уверен, что разделителем всегда будет пробельная последовательность - то да, тут пустых строк не может быть.
@@SayXaNow а ну кстати да, почему-то сразу не подумал про то что сплитовать придется не только по пробелам, так программа более универсальная получается, спасибо!
у меня вопрос, а можно ли в данном примере использовать для проверки декоратор staticmethod?
не совсем понимаю почему тут используем classmethod
да, верно, если нет обращений к атрибуту класса, то staticmethod достаточно и предпочтительно
@@selfedu_rus спасибо!
Можно тупой вопрос? Почему в коде этого урока нет собственно декоратора property? Хотя в названии ролика он есть
Есть декоратор. Ты точно всё видео смотрел?
@@PythonSthenics Пересмотрел и действительно - есть. Извиняюсь, был не прав. Видимо это эффект от обучения - смотрю как баран на новые ворота, в упор многое не замечаю.
В волшебных методов __setatr, __getatr, __getatribute тоже можна писать проверки и так же использывать как Вы в этом уроке. Я понимаю, что можна писать по-разному, но эти волшебные методы тогда более пригодятся в более сложных примерах или можна так и так как нравится писать? Хочу научится понимать тонкости. Спасибо
тоже интересно
Здравствуйте! Хотел уточнить, в плане структуры не лучше было создать дочерний класс с валидацией этих данных и поместить его в другой файл?(А в основной - этот, импортировать)
Если валидация привязана исключительно к данным класса Person, то городить огород из иерархий не вижу смысла. Если у вас планируется некий универсальный проверочный класс, то можно реализовать его как миксин.
Спасибо за видео. Скажите, а свойства класса S_RUS и S_RUS_UPPER не лучше сделать приватными и обращаться к ним через соответствующий геттер? Заранее спасибо.
Да, только обращаться напрямую, т.к. мы же внутри класса с ними работаем.
@@selfedu_rus ну да, логично. Спасибо за ответ.
я не могу понять, зачем вы проверяете наличие минимум 1 символа, хотя перед этим проверяли наличие 3 элементов, ведь если будет меньше 1 символа, то наверное будет меньше 3 элементов, разве нет?
!= 3 - это 0, 1, 2, 4, ...
Не понимаю. Мы специально сделали поля с __ чтобы к ним нельзя было добраться извне, а теперь делаем так чтобы это было можно сделать?
к полям __ нельзя обращаться извне (это не защита от злоумышленника, а предупреждение для программиста), внутри методов класса можно
@@selfedu_rus это я понял. Но мы же их делали для инкапсуляции. То есть чтобы программист, который будет работать с нашим классом знал, что данное поле должно быть использовано внутри класса и работать с ним извне не надо(на сколько я понял) . Но теперь с помощью property мы делаем так чтобы могли обращаться к этим полям извне. Пока писал появилось предположение. Это для того чтобы мы могли читать и записывать их каким то особым образом через методы для добавления и чтения?
Я запутался, помогите! В предыдущем уроке сказано, что @property применяется для того, чтобы не плодить def get_ и def set_ для каждого атрибута. Но в этом видео мы так-же создаем геттер и сеттер для каждого атрибута, но еще и с доп строкой в виде @property или @.setter. Смысл?
Не совсем верно. Property - это особый тип (класс) данных. Переменные этого типа могут использоваться в выражениях так же, как и привычные типы данных (str, int) и т.д. Что более интуитивно и понятно.
Если radius - наше свойство типа Property, то мы легко можем писать выражения вида:
a.radius = 100 / (k + b + c)
length = 8 * math.pi * a.radius
Вместо:
a.set_radius(100 / (k + b + c))
length = 8 * math.pi * a.get_radius()
ключевой особенностью этого типа данных является то, что он имеет свой собственный настраиваемый сеттер и геттер, который будет вызван при обращении к переменной этого типа. Что позволяет помимо банального извлечения или присвоения значения, прикрутить какую угодно дополнительную логику в сеттер и геттер. Например, сеттер автоматически может проверять корректность данных, и выдавать ошибку, если присваивается отрицательное значение.
PS и да, в питоне на самом деле нет привычных типов данных как в других языках программирования. str, int и т.д. - это объекты (классы), через которые и происходит «симулирование» типов данных. Все в питоне - это объекты, функции - тоже объекты. Когда осознаешь это, станут понятны многие вещи, над которыми раньше не задумывался.
s = "hello world" - создание класса str и присвоение ему значения "hello world"
s.partition(" ") - вызов метода класса s
(4 + 7).bit_length() - создание класса int, присвоение ему значения 11 и вызов метода класса bit_length(), для получения длины значения в битах.
---------------------
Создание собственного класса, который будет обладать всеми свойствами строкового типа данных и дополнительным методом транслитерации:
class my_string(str):
def transliterate(self):
d = {"р": "r", "в": "v"}
return "".join([d[x] if x in d else x for x in self.__str__()])
s = my_string("Привет, мир!")
print(s.count("и")) - унаследовано от базового класса str
print(s.transliterate()) - применение нашего собственного метода.
Видно что не тугодум, обьяснения качественные, только какие задачи с этими методами можно решать, как практически применять все это?
я немного не понял, для чего здесь строчка @classmethod? Почему нельзя было делать без нее с помощью обычных функций?
Можно. Но обычные методы первым параметром принимают self, а здесь нам это не нужно. Поэтому и прописал @classmethod
почему не статикметод используется для проверки возраста и веса?
да лучше статик
👍
Назрел вопрос. Если я буду пользоваться не @classmethod а обычным методом, но в параметре укажу cls - эту будет считаться плохим тоном или из этого в будущем можно получить ошибку. В данной программе все работает без ошибок.
работать будет без ошибок, но да, это плохой тон ))
@@selfedu_rus Классно, что есть обратная связь. Попытался доработать форму, чтобы можно было вводить данные не в коде а в консоли. Прописал строку g = Person(input(), input(), input(), input()), но программа пропускает числовые инпуты. Пробовал обернуть числовые int() и float(), выдает ошибку ValueError: invalid literal for int() with base 10: '' Подскажите что не так?
@@Receive_ функция int(val) может преобразовать только строки с целыми числами
чтобы не прописывать все символы которые мы хотим проверить можно еще так прописать проверку через регулярные выражения
import re
if re.search(r'[^А-Яа-яЁё\s\-]', fio) != None:
raise TypeError('Допустимы только Буквы, тире и пробел!')
Так же подумал и сделал, но вот вопрос, класс же теперь за собой тянет Re модуль, и нельзя им воспользоваться его нţимпортируя. Как вы думаете, имеет ли это значение?
Вместо "А-Яа-яЁё" работает "Ё-ё" - ещё короче.
@@RealMineManUK , ну модуль string Сергей импортирует без зазрений совести, так что не в том дело. re - тоже стандартная библииотека. Сергей писал, что regular expressions не использует, чтоб отдельно не объяснять, что это такое тем, кто не в курсе.
почему у меня в коде пишет ошибку с буквой s
В чем смысл проверки длины слова на
@@selfedu_rus та не, я может неправильно написал. Напишите мне условие при котором мы вводим ФИО и выдает ошибку " в ФИО должен быть хотя бы один символ", пожалуйста, а то все равно не понимаю целесобразность этой проверки. Как по мне, она взаимоисключается при проверки на длину ФИО из 3 слов и что все символы буквы.
СПАСИБО ВАМ, ЗА ВАШ ТРУД!
Я хоть и не все понимаю, для меня не хватает домашних заданий....прям очень сильно!
@@bigsmilefriend курс на Stepik делаю, недели 2-3 и будет
Можно использовать в classmetod вот так(если Python 3.10):
@classmethod
def verify_weight(cls, weight: int | float):
if not isinstance(weight, int | float) or weight < 20:
raise TypeError(".......")
извините за мой руский, если есть ошибки.
текст про staticmethod, а код classmethod.....
Можно было проверять ФИО через регулярные выражения намного код проще стал бы
'абвгдеёжзийклмнопрстуфхцчшщъыьэюя-'
Это должен быть первый коментарий)
А зачем метод валидации возраста, веса и паспорта декорированы как classmethod, он же с данными класса не работают. И их поидее лучше staticmthod декорировать. И валидация ФИО тоже под вопросом. Данные класса же не изменяются, а только берутся для проверки символов, почему его простым методом не оставить. Мб я что-то не понимаю?
согласен!
А для нужен был декоратор @classmethod? Вы же говорили, что @classmethod используется в основном для статических методов.
Ещё хотел сказать из опыта, что отчество всегда должно быть необязательным полем, так как у некоторых народов отчество является частью имени (поэтому отчество у них отсутствует), например, у монголов, тюрков и т. д.
Через @classmethod мы описываем методы, которые работают только с атрибутами класса, а не его экземпляров. С отчеством да, согласен, ну это учебный пример )
Не совсем понятно применение в данном примере методов класса (за исключением verify_fio, т.к. в этом методе есть обращение к атрибутам класса)
Почему именно методы класса, а не статические методы?
строгих правил тут нет, но если мы в методе не собираемся работать с атрибутами объектов, то это кандидат на класс-метод. Статические вообще очень редко задаются и, как правило, это какие то сторонние вспомогательные функции, связанные как то с тематикой класса и не более того. С опытом придет понимание.
@@selfedu_rus спасибо за ответ!
Пытаюсь понять когда что используется.
Считал, что если не работаем ни с атрибутами класса, ни с атрибутами экземпляра класса, то это и есть безальтернативный вариант статического метода.
А не проще проверять корректность символов так: set(s) < set(letters)?
да, это хороший подход, только здесь
Немного позанудствую. Возраст по английски будет age, а old - это старый. Но, в любом случае спасибо за урок.
Созрел вопрос после просмотра лекций - Как сделать такой же единый интерфейс(сеттер, геттер) но чтоб сеттер мог принимать не одно значение а несколько?
Я пробовал с помощью проперти но как я понял там можно передавать только одно значение.. Ну или если я что-то упустил то поправьте меня :)
через кортежи
а зачем тут проперти, могли бы оставить просто так, просто чтобы вызов сеттера и геттера был публично обозначен и все что ли ??
Пока досконально не проверял, но на первый взгляд в проверке ФИО есть загвоздка с пробелами. По логике они должны быть раз используется split без аргументов, но в verify_fio они не разрешены.
Хотя похоже проглядел, что в русских буквах в конце стоит пробел. Так что все ОК.
Сергей Михайлович, а Вам точно 30?
Разве в программировании все начинается не с 0?
Это ты к чему?
1:08 а если я Хасбик?)
Только непонятно зачем верификаторы делать методами класса.
Бедный Сергей Михайлович...
70 лет проработал в одной организации...
Это такая своя традиция когда пишешь на питоне, называть переменные одной буквой, чтоб через пять минут не помнить, что это? Не у первого автора это вижу, глаза режет и душу рвёт.
В данном случае это такой проект, который на след день не понадобится, поэтому так можно
@@Majohne можно показывать как делать не правильно, но не предупреждая, что это не правильно? По этой логике потом люди так же код и пишут, как увидели.
@@nikolaydemchenko6741 вряд-ли человек будет изучать ООП, если не изучил базу языка, в уроках которой обычно это все объясняют. Хотя мб есть и такие, кто при объяснении самого языка это используют и не говорят об этом
@@Majohne если ты пишешь код, и много, ты не будешь страдать такой ерундой типа тут и одной буквой сойдёт. У тебя в голове такое не возникнет в принципе, ты просто назовешь переменную максимально понятно и не будешь об этом вообще думать. И в этом курсе не совсем про ООП. Тут про реализацию классов и объектов в данном языке. Хотя многие думают, что это уже ООП.
ёкарный бабай!
Где-то в мире заплакала одна Жанна Кальман.
Немного некрасиво использовать if type(fio) != str, в доках больше рекомендуется использовать isinstance.
Все зависит от задачи. Здесь да, можно isintance, но вот для проверки булевых величин isintance может возвращать неверный результат, т.к. выполняет определение типа с учетом наследования. Функция type более строгая.
13:20
Можно устроить проверку на символы и тире сделать вот так
all([all(y.isalpha() for y in x.split()) for x in fio.split('-')])
Всё чудесно и понятно, НО у меня почему то выскакивает ошибка на строке 12 self.verify_weight(weight) после запуска пишет line 12, in __init__ self.verify_weight(weight)
AttributeError: 'Person' object has no attribute 'verify_weight'. Did you mean: 'verify_weght'?
вот выложу весь код:
from string import ascii_letters
class Person:
S_RUS = 'абвгдеёжзийклмнопрстуфхцчшщьыъэюя-'
S_RUS_UPPER = S_RUS.upper()
def __init__(self, fio, old, ps, weight):
self.verify_fio(fio)
self.verify_old(old)
self.verify_ps(ps)
self.verify_weight(weight)
self.__fio = fio.split()
self.__old = old
self.__ps = ps
self.__weight = weight
@classmethod
def verify_fio(cls, fio):
if type(fio) != str:
raise TypeError("ФИО должно быть строкой")
f = fio.split()
if len(f) != 3:
raise ValueError("Неверный формат ФИО")
letters = ascii_letters + cls.S_RUS + cls.S_RUS_UPPER
for s in f:
if len(s) < 1:
raise TypeError("В ФИО должен быть хотя бы один символ")
if len(s.strip(letters)) != 0:
raise TypeError("В ФИО можно использовать только буквенные символы и дефис")
@classmethod
def verify_old(cls, old):
if type(old) != int or old < 14 or old > 120:
raise TypeError("Возраст должен быть целым числом в диапазоне [14, 120]")
@classmethod
def verify_weght(cls, w):
if type(w) != float or w < 20:
raise TypeError("Вес должен быть вещественным числом от 20 и выше")
@classmethod
def verify_ps(cls, ps):
if type(ps) != str:
raise TypeError("Паспорт должен быть строкой")
s = ps.split()
if len(s) != 2 or len(s[0]) != 4 or len(s[1]) != 6:
raise TypeError("Неверный формат паспорта")
for p in s:
if not p.isdigit():
raise TypeError("Серия и номер паспорта должны быть числами")
@property
def fio(self):
return self.__fio
@property
def old(self):
return self.__old
@old.setter
def old(self, old):
self.verify_old(old)
self.__old = old
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, weight):
self.__weight = weight
@property
def passport(self):
return self.__passport
@passport.setter
def passport(self, ps):
self.verify_ps(ps)
self.__passport = ps
p = Person('Балакирев Сергей Михайлович', 30, '1234 567890', 80.0)
p.old = 100
p.password = '4567 123456'
p.weight = 70.0
print(p.__dict__)
у тебя название функции verify_weght (пропустил "i")