Найти - Пользователи
Полная версия: Как правильно инициализировать класс
Начало » Python для новичков » Как правильно инициализировать класс
1
Ematten
Добрый день.

Минимальный пример для понимания:

class Server:
def __init__(self, name):
if какое-то условие:
self.property = value0
else:
self.property = value1

Пример крайне упрощен. На самом деле может быть большое кол-во условий. Правильно ли с точки зрения pythonic way и в принципе читаемости кода и подводных камней так делать? Или есть какие-то другие варианты (например, разные классы для разных условий и т.д).
py.user.next
Ematten
Правильно ли с точки зрения pythonic way и в принципе читаемости кода и подводных камней так делать? Или есть какие-то другие варианты (например, разные классы для разных условий и т.д).
Да как бы в двух словах не расскажешь.

Ematten
Как правильно инициализировать класс
Да класс-то инициализировать нельзя. Инициализировать можно экземпляр класса только. А класс - это множество объектов, у которых совпадает структура, поведение и семантика. Между объектами - одни взаимодействия, между классами - другие отношения.

Так что ты даже определений не знаешь, каких-то базовых понятий. При таком раскладе даже если ты кучу классов напишешь в коде, это тебе никак не поможет в том, чтобы программа работала. Будет просто куча классов ни к селу ни к городу.

Ematten
Или есть какие-то другие варианты (например, разные классы для разных условий и т.д).
Не, ты создаёшь один класс Server, потом инстанциируешь объект этого класса. И когда объект создался, ты в него подаёшь снаружи данные для инициализации значений свойств. Когда значения свойств объекта меняются, объект переходит в другое состояние. Соответственно, тебе нужно представить и описать всё множество состояний объекта. Одно состояние объекта образуется из всех значений всех свойств объекта в один момент времени.

Например, у тебя есть класс Собаки. И этому классу принадлежит объект Собака. Так вот собака может спать, а может бодрствовать, может быть весёлой, может быть грустной. И вот если она бодрствует и весёлая - это одно состояние, если она бодрствует и грустная - это второе состояние, если она спит и весёлая - это третье состояние, если она спит и грустная - это четвёртое состояние. Как видишь, параметров два, а состояние одно всегда. Вот так образуется одно состояние объекта. То есть оно не в одном свойстве заключается, а во всех сразу.

Когда объект создаётся, он находится в исходном состоянии. Это и нужно занести в процедуру инициализации объекта. А дальше состояние объекта может меняться из-за выполнения операций над этим объектом. Чтобы выполнить операцию над объектом, в питоне нужно вызвать метод у этого объекта. Ты вызываешь какой-то метод и состояние объекта меняется с одного состояния на другое.

Был у тебя сервер, например, и он ничего не делал. Ты просто создал его и всё. А потом ты вызвал у него метод listen(). Это ты выполнил операцию над объектом сервером. В результате выполнения этой операции над объектом сервером объект сервер поменял своё состояние с исходного на прослушивающее. И дальше ты к нему обращаешься и спрашиваешь “слышишь что-нибудь?”. Это тоже делается через выполнение операции над этим объектом сервером. В результате выполнения этой операции над объектом сервером, состояние сервера не меняется, но он тебе возвращает данные в виде ответа “да, слышу”. Понимаешь, не все операции, выполняемые над объектом, меняют его состояние.

Вот такие дела.

И чтобы знать это всё, надо читать книжки. Само оно ниоткуда не осенит тебя.
Ematten
Спасибо за пояснения. В терминологии разбираюсь достаточно, написал неправильно, согласен. Инициализация объекта класса правильно.

Я говорю о других случаях. Понятно, что нет смысла заводить два отдельных класса “Собака” и “Черная собака”, потому что второй класс будет дублировать первый. Для этого можем ввести свойство объекта “цвет собаки”. У меня немного другая ситуация. Допустим у меня есть необходимость инициализировать два объекта класса “Собака”, при этом первый - новая собака, а вторая - собака, свойства которой уже были описаны и занесены в базу данных. Для инициализации второго объекта мне нужно будет дергать БД. В этом случае есть ли смысл использовать два класса “Новая собака” и “Знакомая собака” или достаточно все описать в одном классе? Я спрашиваю с т. з. целесообразности, читаемости и прозрачности кода.

robisho
первый класс Собака у тебя будет родительский, второй класс ЗнакомаяСобака ты будешь наследовать от Собаки, а третий класс ЗнакомаяСобакаТерьер будешь наследовать от ЗнакомойСобаки со всеми ее свойствами.
xam1816
Я представляю Класс как некий шаблон или матрица, отдельное пространство.
Атрибуты класса типа
self.variable_1 = какие-то данные
self.variable_2 = еще какие-то данные

это как ячейки для данных в оперативной памяти.
Т.е когда инициализируешь сам экземпляр, или объект этого класса, то кладешь в эти ячейки данные, согласно их назначению.

например
  
class Display:
    def __init__(self, background, width):
        self.background = background
        self.width = width
        self.current_message = ''

это класс дисплей, а какой это именно дисплей? Вот когда загружаешь в него данные этого конкретного дисплея, тогда он и становиться конкретным экземпляром этого класса - дисплей

  
disp_1 = Display('black', 5)
disp_2 = Display('blue', 10)

Т.е вот теперь есть два пространства в оперативной памяти, созданных по шаблону дисплей, хранящие информацию в виде цвета фона и ширины

Но хранить в оперативной памяти какие-то данные бессмысленно, если с ними не работаешь, для хранения существуют базы данных или отдельные файлы например.

Так зачем тогда мы загрузили эти данные в оперативную память?
Чтобы этой информацией манипулировать, оперативно использовать ее.

Поэтому делаем для нее определенные команды - методы, по которым эта информация будет или меняться или как-то по другому использоваться

добавим методы, для вывода сообщения на дисплей, и метод получения текущего сообщения.

  
class Display:
    def __init__(self, background, width):
        self.background = background
        self.width = width
        self.current_message = ''
 
    def message_display(self, text):
        self.current_message = text
        print(f'на дисплей шириной {self.width} вывелся текст {text}, цвет фона {self.background}')
 
    def get_current_message(self):
        return self.current_message
 
def main():
    disp_1 = Display('black', 5)
    disp_2 = Display('blue', 10)
 
    for d in disp_1, disp_2:
        d.message_display('HELLO')
 
if __name__ == '__main__':
    main()

 
на дисплей шириной 5 вывелся текст HELLO, цвет фона black
на дисплей шириной 10 вывелся текст HELLO, цвет фона blue

Т.е что нам дает эта конструкция:
а) загрузка нужных данных в отдельное изолированное пространство
b) манипуляция этими данными, заранее подготовленными методами

И это выше не ООП!!!


py.user.next
Ematten
Понятно, что нет смысла заводить два отдельных класса “Собака” и “Черная собака”, потому что второй класс будет дублировать первый.
Нельзя просто так, по названиям, сказать, что такие-то классы нужны, а такие-то классы не нужны. Даже если эти названия очень похожи и кажутся логически связанными как-то. Это не показатель.

Вообще, есть три понятия: абстракция, объект, класс. Это всё разные понятия, хотя их легко спутать, и часто даже профессионалы, которые изучают ООП по своим ощущениям от чьего-то кода, с которым работают на работе, а не тонко разбираясь в теории и всех деталях досконально, путают эти понятия и сливают всё в одну кашу. Недавно я слышал от одного профессионала, что он там тоже состояние у класса делает какое-то, хотя речь шла об экземпляре класса. То есть он путает потому, что он изучал ООП по экспериментам с кодом, а не по скучным книгам с математическими доказательствами (для этого нужно образование, чтобы понимать, о чём пишут в таких книгах и что там за уравнения). Кажется, что это несущественная оговорка у него, ерунда. Но на самом деле, это говорит о том, что у него нет фундаментального видения, через которое обычно и видишь множество объектов в качестве одного скопления, а множество классов в качестве другого скопления.

Понимаешь, из-за этого вот, из-за этой каши, человек и не может отделить связи между классами и связи между объектами этих классов. Об абстракциях, которые формируются вообще до формирования каких-то классов и даже объектов, у них вообще речи нет, хотя абстракции - это фундамент для зарождения информационной системы. С абстракций всё начинается.

Допустим, у тебя есть стена и тень на стене. Тень на стене попадает на трещину в стене и становится темнее в этом месте. Так вот границы трещины в стене и границы трещины в тени на стене - это разные границы. Одни границы описывают, докуда будет продолжаться светлая тень на стене. Другие границы описывают, докуда будет продолжаться тёмная тень внутри трещины. Казалось бы, это хрень какая-то, там можно и дальше углубляться в детали, но если ты играл в компьютерные игры, то ты видел, что когда юнит подходит к двери с автоматом, то он дулом автомата протыкает дверь насквозь и его просто видно по этому дулу, вылезающему сквозь дверь, что он сейчас за дверью стоит и ждёт, когда ты её откроешь, и можно просто, не открывая дверь, стрелять либо сквозь неё или прямо ему в дуло автомата, чтобы его завалить там за дверью. При этом у юнита полная уверенность, что ты его не видишь, так как дверь не открыта и визуального контакта из-за этого между вами нет.

Так вот, не факт, что Собаки и ЧёрныеСобаки как классы находятся в отношении наследования. А если и находятся, то ты так сразу и не скажешь, кто от кого происходит Собаки от ЧёрныхСобак или ЧёрныеСобаки от Собак. Потому что вопреки логике может быть и так и так. Можно даже найти такую иерархию, где ЧёрныеСобаки стоят выше Собак и Собаки происходят от них. Для этого можно использовать концептуальную кластеризацию либо вообще теорию прототипов. Например, если кластеризация производится по признаку максимальной похожести на чёрное тело, то ЧёрныеСобаки больше похожи на черное тело, чем просто Собаки. И тогда мы можем сказать, что ЧёрныеСобаки стоят на вершине иерархии наследования, а КрасныеСобаки и Собаки являются производными классами от ЧёрныхСобак. У одной цвет стирали, у другой чёрный цвет перекрашивали в красный. То есть речь идёт о том, что не вся логика укладывается в логику реального мира. Тебе может казаться, что нельзя дверь проткнуть автоматом, потому что это невозможно в реальном мире, а в компьютерных играх (программах) мы встречаем это сплошь и рядом и можем этим пользоваться.

Ematten
Для этого можем ввести свойство объекта “цвет собаки”.
А если у собаки несколько цветов сразу? Это всё не так просто. Хочется, конечно, уложить всё в простые модели, как в жизни - всё легко и просто. Но в мире программ это всё не так устроено. Поэтому не торопись создавать свойство, если тебе показалось, что оно очень логично. Оно может вылезти потом боком. Сначала надо всё хорошенько обдумать. Может, не один раз.

Ematten
Допустим у меня есть необходимость инициализировать два объекта класса “Собака”, при этом первый - новая собака, а вторая - собака, свойства которой уже были описаны и занесены в базу данных. Для инициализации второго объекта мне нужно будет дергать БД. В этом случае есть ли смысл использовать два класса “Новая собака” и “Знакомая собака” или достаточно все описать в одном классе?
Не, у тебя один класс Собаки, а у этого класса есть операция заполнения объекта Собака данными. И вот туда надо подать данные или сразу, или взяв их из базы данных. Так что объект Собака не будет знать о существовании базы данных какой-то там в соответствии с законом Деметры. Если объект Собака не знает, откуда появились данные в нём, - это хорошо. Если объект Собака знает, откуда появились данные в нём, - это плохо. Плохо это потому, что эта информация бесполезна для этого объекта, а при этом внедрить туда что-то другое вместо базы данных будет сложно, потому что у тебя все Собаки, Кошки и Лошади будут знать про эту базу данных и их придётся всех переделывать капитально, чтобы они про неё знать перестали. Закон Деметры защищает от всей этой возни, поэтому его надо соблюдать везде и во всём. Звучит он так: меньше знаешь - крепче спишь.

А полиморфный конструктор, который делается в таких случаях на базе ad-hoc полиморфизма, который там можно сделать на случай разной инициализации экземпляра, идёт уже потом в качестве решения. Сначала нужно определить, нужно ли его делать вообще. Не надо бросаться сразу на это. Можно просто пропереться, наделать всякой лажи красивой внешне, чтобы быть похожим на профессионала (по оценке дураков всяких, рассматривающих фантики с открытыми ртами), а потом обосраться просто в тот момент, когда надо будет исправить небольшой бажок, вылезший потом впоследствии, или небольшой недостаток архитектуры, который всё портит годами потом. Понимаешь, да, если в фундамент заложить красивый кирпич с трещиной внутри, ничего не проверить перед этим, а просто довольствоваться внешней красотой это кирпича и думать о рекламе своих услуг, как она попрёт, когда все увидят этот кирпич в твоей кладке, то он будет смотреться красиво очень, но когда дом будет построен и этот красивый кирпич лопнет со временем из-за той трещины, ты очень пожалеешь, что закладывал его тогда, чисто чтобы про тебя думали, как о прекрасном строителе, и грамоту тебе дали в тот день “лучший строитель Урюпинска в 1973-м году!”, потому что реклама в итоге не сработала, как ожидалось, а дом уже построен и в нём живёшь ты, и очень скоро он из-за лопнувшего кирпича сложится, как карточный домик, со штукатуркой тебе же на голову и всеми остальными прелестями. Не надо делать умные вещи, чтобы внешне что-то там выглядело. Умнее ты от этого не станешь и выглядеть умным ты будешь только у дураков в их компании, которые сами ничего делать не умеют. Надо делать эффективные вещи, которые работают годами и надёжно.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB