Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

В чем отличие в присвоении атрибута класса в конструкторе и нет

#python #ооп


class W:
    a = 1


или 

class W():
  def __init__(self):
    self.a = 1


В чем отличие? И какие фишки есть с каждым вариантом?
    


Ответы

Ответ 1



В первом случае переменная a является атрибутом класса, и доступна и как W.a и как: w = W() w.a Во втором же случае переменная создаётся в момент инициализации объекта, а значит принадлежит объекту и доступна только как: w = W() w.a Основная разница заключается в том, как распределяется память. В первом случае память на хранение данной переменной выделяется при объявлении класса, во втором же случае память выделяется для каждого из объектов класса. Используется и то и другое, всё зависит от задачи. Из фишек - можно например изменять значение сразу для всех объектов класса: class A(): b = 1 a = A() b = A() a.b # результат 1 b.b # результат 1 # Когда меняем значение для члена класса A.b = 5 # Оно изменяется для всех объектов класса b.b # результат 5 a.b # результат 5

Ответ 2



Немного расширю ваш пример: class T: def __init__(self): self.a = 10 self.b = [1, 2, 3] c = 20 d = [15] Когда интерпретатор встречает этот код, он выполняет тело класса и все локальные переменные сохраняет как атрибуты этого класса. Таким образом, мы имеем класс T, у которого есть три атрибута (не считая автоматические): c, d и __init__ (объявление функции тоже создаёт локальную переменную!): >>> T.__dict__ { '__init__': , 'c': 15, 'd': [20], # ... } Когда мы создаём экземпляр класса, выполняется метод __init__ класса, в который передаётся заготовка создаваемого экземпляра. Таким образом, в строке t = T() Мы получаем экземпляр t класса T, который имеет два атрибута: a и b: >>> t.__dict__ { 'a': 10, 'b': [1, 2, 3] } Что происходит дальше, когда мы пытаемся получить доступ к атрибутам? Например, имеем три строки кода: print(t.a) # 10 print(t.c) # 20 print(T.c) # 20 # print(T.a) # error: у класса нет такого атрибута В первой строке интерпретатор посмотрит в атрибуты переменной t, найдёт среди них имя a и вернёт его значение. Во второй строке происходит следующее. Интерпретатор не может найти атрибут c среди атрибутов объекта t, поэтому обращается к объекту класса, экземпляром которого является этот объект. У класса уже имеется нужный атрибут, поэтому на этом поиск завершается, возвращается значение 20. Третья строка выполняется аналогично первой, с условием что атрибут ищется сразу у класса. Четвёртая строка выполнится с ошибкой, потому что атрибуты экземпляров не распространяются на классы. Какие за счёт этого возникают эффекты? Все атрибуты класса доступны всем экземплярам. Мы можем изменять атрибуты или заменять их значения, и это отразится на всех экземплярах класса: t1 = T() t2 = T() print(t1.d, t2.d) # [15] [15] # изменим атрибут класса, пользуясь тем, что список является изменяемым типом t1.d.append(16) print(t1.d, t2.d) # [15, 16] [15, 16] # создадим новый атрибут уровня класса T.x = "Hello!" print(t1.x, t2.x) # Hello! Hello! Важно помнить, что атрибуты экземпляра класса скрывают атрибуты класса: t1.d = [-1, -2] print(t1.d, t2.d, T.d) # [-1, -2] [15, 16] [15, 16] Т.е. в строке t1.d = [-1, -2] не изменяется значение атрибута класса, а создаётся новый атрибут у объекта t1. Поэтому изменить значение неизменяемого атрибута класса через экземпляр этого класса не получится. Если требуется подобная операция, стоит изменять атрибут класса. В итоге имеем следующее: при использовании атрибутов класса стоит проявлять осторожность с изменяемыми объектами (изменение в одном экземпляре затронет все остальные), а также со скрытием атрибутов класса атрибутами экземпляра класса. С учётом этих замечаний обычно рекомендуется создавать все атрибуты в методе __init__ класса, а не в самом классе. Иногда в самом классе хранят константные значения, относящиеся ко всем экземплярам, например, значения по умолчанию.

Комментариев нет:

Отправить комментарий