верно ли что атрибут класса перекрывает атрибут объекта
В чем отличие в присвоении атрибута класса в конструкторе и нет
В чем отличие? И какие фишки есть с каждым вариантом?
2 ответа 2
В первом случае переменная a является атрибутом класса, и доступна и как W.a и как:
Во втором же случае переменная создаётся в момент инициализации объекта, а значит принадлежит объекту и доступна только как:
Основная разница заключается в том, как распределяется память. В первом случае память на хранение данной переменной выделяется при объявлении класса, во втором же случае память выделяется для каждого из объектов класса. Используется и то и другое, всё зависит от задачи.
Немного расширю ваш пример:
Когда интерпретатор встречает этот код, он выполняет тело класса и все локальные переменные сохраняет как атрибуты этого класса.
Когда мы создаём экземпляр класса, выполняется метод __init__ класса, в который передаётся заготовка создаваемого экземпляра. Таким образом, в строке
Что происходит дальше, когда мы пытаемся получить доступ к атрибутам?
Например, имеем три строки кода:
Третья строка выполняется аналогично первой, с условием что атрибут ищется сразу у класса.
Четвёртая строка выполнится с ошибкой, потому что атрибуты экземпляров не распространяются на классы.
Какие за счёт этого возникают эффекты?
Все атрибуты класса доступны всем экземплярам. Мы можем изменять атрибуты или заменять их значения, и это отразится на всех экземплярах класса:
Важно помнить, что атрибуты экземпляра класса скрывают атрибуты класса:
В итоге имеем следующее: при использовании атрибутов класса стоит проявлять осторожность с изменяемыми объектами (изменение в одном экземпляре затронет все остальные), а также со скрытием атрибутов класса атрибутами экземпляра класса.
С учётом этих замечаний обычно рекомендуется создавать все атрибуты в методе __init__ класса, а не в самом классе.
Иногда в самом классе хранят константные значения, относящиеся ко всем экземплярам, например, значения по умолчанию.
Примеры работы с классами в Python
Python — объектно-ориентированный язык с начала его существования. Поэтому, создание и использование классов и объектов в Python просто и легко. Эта статья поможет разобраться на примерах в области поддержки объектно-ориентированного программирования Python. Если у вас нет опыта работы с объектно-ориентированным программированием (OOП), ознакомьтесь с вводным курсом или учебным пособием, чтобы понять основные понятия.
Создание классов
Пример создания класса на Python:
Создание экземпляров класса
Доступ к атрибутам
Теперь, систематизируем все.
При выполнении этого кода, мы получаем следующий результат:
Вы можете добавлять, удалять или изменять атрибуты классов и объектов в любой момент.
Вместо использования привычных операторов для доступа к атрибутам вы можете использовать эти функции:
Встроенные атрибуты класса
Для вышеуказанного класса давайте попробуем получить доступ ко всем этим атрибутам:
Когда этот код выполняется, он возвращает такой результат:
Удаление объектов (сбор мусора)
Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. С помощью процесса ‘Garbage Collection’ Python периодически восстанавливает блоки памяти, которые больше не используются.
Сборщик мусора Python запускается во время выполнения программы и тогда, когда количество ссылок на объект достигает нуля. С изменением количества обращений к нему, меняется количество ссылок.
Пример работы __del__()
Деструктор __del__() выводит имя класса того экземпляра, который должен быть уничтожен:
Когда вышеуказанный код выполняется и выводит следующее:
Наследование класса в python
Наследование — это процесс, когда один класс наследует атрибуты и методы другого. Класс, чьи свойства и методы наследуются, называют Родителем или Суперклассом. А класс, свойства которого наследуются — класс-потомок или Подкласс.
Вместо того, чтобы начинать с нуля, вы можете создать класс, на основе уже существующего. Укажите родительский класс в круглых скобках после имени нового класса.
Класс наследник наследует атрибуты своего родительского класса. Вы можете использовать эти атрибуты так, как будто они определены в классе наследнике. Он может переопределять элементы данных и методы родителя.
Синтаксис наследования класса
Классы наследники объявляются так, как и родительские классы. Только, список наследуемых классов, указан после имени класса.
В чем разница между атрибутами класса и экземпляра?
есть ли какое-либо значимое различие между:
Если вы создаете много экземпляров, есть ли разница в производительности или требования к пространству для двух стилей? Когда вы читаете код, считаете ли вы, что значение двух стилей значительно отличается?
6 ответов
помимо соображений производительности, существует значительное семантический разницы. В случае атрибута class есть только один объект, на который ссылаются. В instance-attribute-set-at-instantiation может быть несколько объектов, на которые ссылаются. Например,
разница в том, что атрибут класса является общим для всех экземпляров. Атрибут экземпляра уникален для этого экземпляра.
Если исходить из C++, атрибуты в классе больше похожи на статические переменные-члены.
вот очень хороший в должности, и резюме его, как показано ниже.
и в наглядной форме
присвоение атрибутов класса
если атрибут класса задан путем доступа к классу, он переопределит значение для все экземпляры
если переменная класса задана путем доступа к экземпляру, она переопределит значение только для этого экземпляра. Это по существу переопределяет переменную класса и превращает ее в переменную экземпляра, доступную интуитивно, только для этого экземпляра.
когда вы будете использовать атрибут класса?
хранение констант. Как атрибуты класса могут быть доступны как атрибуты класса, его часто используют их для хранения Константы класса
определение значений по умолчанию. Банальный пример, мы можем создать ограниченный список (т. е. список, который может содержать только определенное количество элементов или меньше) и выбрать шапку по умолчанию 10 товаров
поскольку люди в комментариях здесь и в двух других вопросах, отмеченных как dups, все, похоже, путаются в этом одинаково, Я думаю, что стоит добавить дополнительный ответ поверх Алекс Ковентри!—22—>.
тот факт, что Алекс присваивает значение изменяемого типа, как список, не имеет ничего общего с тем, являются ли вещи общими или нет. Мы можем видеть это с помощью или is оператор:
надеюсь, теперь понятно, почему Алекс использовал списка: тот факт, что вы можете мутировать список, означает, что легче показать, что две переменные называют один и тот же список, а также означает, что в реальном коде важнее знать, есть ли у вас два списка или два имени для одного и того же списка.
** некоторые люди используют это, используя атрибут класса в качестве» значения по умолчанию » для атрибута экземпляра, который экземпляры могут или не могут установить. Это может быть полезно в некоторых случаях, но это также может быть запутанным, поэтому будьте осторожны с ним.
просто уточнение о том, что сказал Алекс Ковентри, другой Алекс (Мартелли) обратился к аналогичному вопросу о comp.lang.python группа Новостей лет назад. Он исследует семантическое различие того, что человек намеревался и что он получил (используя переменные экземпляра).
есть еще одна ситуация.
тот же тип доступа к экземпляру через класс или экземпляр возвращает другой результат!
объяснить
если атрибут найден в словарь классов, которые составляют. объекты MRO, затем проверьте, указывает ли искомый атрибут на дескриптор данных (который является не более чем классом, реализующим оба __get__ и __set__ методов). Если это так, разрешите поиск атрибута, вызвав __get__ метод дескриптора данных (строки 28-33).
Методы класса и статические методы
Когда интерпретатор достигает инструкции class (а не тогда, когда происходит вызов класса), он выполняет все инструкции в ее теле от начала и до конца. Все присваивания, которые производятся в ходе этого процесса, создают имена в локальной области видимости класса, которые становятся атрибутами объекта класса.
Благодаря этому классы напоминают модули и функции:
Для работы с классом питон создает отдельный объект, описывающий весь класс целиком как набор правил, а не отдельный экземпляр класса. (class object).
У этого объекта тоже могут быть свои поля и методы.
Они нужны для атрибутов и методов, которые относятся не к конкретному экземпляру, а ко всему классу целиком.
Например, для класса, описывающих дату, это может быть список названий месяцев.
Переменные класса
Попробуем посчитать, сколько экземпляров класса Circle было создано за время работы программы.
При чтении переменных идет поиск этой переменной в пространстве имен.
При = изменяется сам объект. (Быть может создается атрибут этого объекта).
В классе и экземпляре класса может быть поле с одинаковым именем (не пишите так!):
Вызов методов
Для любого объекта класса класс допустимы варианты вызова метода экземпляра класса:
методы класса и статические методы
@classmethod можно переопределить в наследнике класса
У метода класса есть cls, но нет self
@staticmethod нельзя переопределить при наследовании классов
Пример static factory
Допустим, у нас должно быть не более 1 экземпляра данного класса.
Когда какой метод делаем?
Как создавать классы в Python со знанием дела: разбираем на примерах
Так что Python — это (n+1)-й язык, в котором была реализована концепция ООП.
Что же такое ООП?
В какой-то момент мы поймем, что утка чем-то похожа на других птиц: значит, ее можно отнести к категории (обобщенному, родительскому классу, шаблону) «птицы». С другой стороны, для нашей игры не важно, как устроен организм утки: главное, что она как-то выглядит и как-то крякает. То есть мы абстрагируемся от ненужных для нашей модели нюансов. А кто-то другой добавит эти нюансы в свою модель и реализует, например, утиный анатомический атлас. Но некто третий использует обе модели и на их базе создаст свою. Это в двух словах о том, каким образом формируются модели и каким образом объекты подразделяются на классы.
Утка и Птица — это классы, а утка1, утка2 и птица1, птица2 — объекты (экземпляры классов).
Концепция объектно-ориентированного программирования опирается на три известных утки кита. Давайте познакомимся с ними поближе.
Инкапсуляция
Этим словом обозначают сокрытие нюансов поведения объекта или его характеристик от «посторонних» программистов. Например, над компьютерной игрой работают два программиста. Один из них реализовал класс Утка, а другой просто хочет его использовать и у них случается такой диалог:
— Какой код написать, чтобы твоя утка полетела?
Наследование
В примере выше Утка — наследник класса Птица. В то же время Птица — родительский класс по отношению к Утке. Вот так принято выражаться.
Полиморфизм
Сколько способов есть у утки, чтобы поздороваться с вами голосом? Наверное, она просто скажет что-то вроде «кря». Допустим, у нее есть один способ. Хорошо, сколько способов есть у лебедя? Они вроде как просто шипят. Допустим, тоже один. А если у птиц в целом? Ну не знаю… много. Однако все в целом по-прежнему будет называться словом «поздороваться»:
Действие называется одинаково, но выполнено оно будет по-разному — в зависимости от объекта, который это действие будет выполнять. У объекта утка1 класса Утка и у объекта лебедь1 класса Лебедь это произойдет по-разному. В буквальном переводе греческое слово polýmorphos означает «многообразный». Поняли, да?
Далее перейдем к практике и разберемся, как создавать классы и делать с ними вот это все на Python. Остальную терминологию ООП и специальные термины из Python изучим по ходу дела.
Первый класс
Ключевое слово class мы уже обсудили. Оно ставится перед названием класса, которое пишется с большой буквы. Внутри класса для начала просто выведем сообщение.
Создаем объект (он же экземпляр класса) вот так — в одну строку:
Имя объекта (здесь создан объект по имени b ) пишем с маленькой буквы. В этом случае внутри скобок нет параметров, но позже мы посмотрим, как и зачем их туда вписывать. Для второго объекта — аналогично. И вот такой результат работы этой программы мы получим:
Птица, ты кто?
Пока мы толком ничего не можем сказать о созданном объекте b. Все потому, что мы создали для него пустой класс, который просто занимается самолюбованием. Давайте это исправим. Добавим один атрибут name классу Bird.
Атрибуты — это набор данных, характеризующих объект или его состояние.
Теперь можно дать каждой птице (объекту класса Bird) имя:
Запустив программу, получим:
Чтобы получить значение атрибута, мы обращаемся к нему через имя объекта с точкой (b.name).
Ключевое слово self
По аналогии внутри него мы будем делать это вот как:
Получается, что self заменяет имя любого объекта, когда мы пишем код внутри его класса.
Если внутри Bird использовать объект другого класса, слово self для него не применяется. Например, создадим класс Cird :
Создадим и используем объект класса Cird внутри класса Bird:
Результат выполнения кода:
Конструктор и инициализатор
Теперь стало немного понятнее, зачем нужен self в этом странном методе класса:
Хорошо… но почему это так? И зачем вообще нужен этот метод?
Это инициализатор. Обычно именно здесь в атрибуты объекта записываются значения. Это могут быть значения по умолчанию (как в классе Cird: self.message = «Я объект c, класса Cird» ) или значения, полученные с использованием параметров функции.
А как же создается сам объект?
Статические и динамические атрибуты
И такое тоже практикуют. И такие атрибуты даже имеют свое название и применение:
Статические атрибуты применяются для того, чтобы иметь одну общую переменную для всех объектов класса.
Дело в том, что при создании новых объектов создаются копии всех динамических атрибутов со сброшенными к «заводским настройкам» значениями. Статические атрибуты относятся не к объекту, а к классу и имеют только одну копию.
В примере выше статический атрибут ruClassName просто хранит название класса. В примере поинтереснее статический атрибут может служить для подсчета количества созданных объектов класса:
Методы класса
Что, если нам дали задачу расширить пример из предыдущего раздела?
1. Нужно добавить Птице больше атрибутов:
2. Реализовать возможность выводить атрибут имя ( name ), а также эти два атрибута для каждого объекта класса Птица.
С первым пунктом мы справимся легко, как говорится, по образу и подобию:
Вроде бы и вывод тоже можно сделать аналогично:
Результат работы кода:
Но наш тимлид считает, что такой код некрасивый и не очень читабельный. Да и ООП рекомендует помещать код обработки данных объекта внутрь его класса. Попробуем:
Функция info() внутри класса Bird называется методом. Теперь у каждого созданного объекта этого класса можно вызвать метод:
Код действительно выглядит более лаконично. Получается, что теперь каждый объект сам может сообщить информацию о себе. И теперь, используя объект, сторонний программист может не задумываться о том, как реализован вывод информации о нем. Он работает, и хорошо!
Результат работы такой же, как в предыдущем примере:
Уровни доступа атрибута и метода
Реализация концепции ООП должна предусмотреть возможность запретить прямой доступ к атрибуту (или к методу) со стороны внешнего кода. То есть сделать его скрытым, приватным (термин из англоязычного ООП — private). Прямая модификация некоторых особо важных атрибутов может привести к дефектам в программе. Часто это нужно для того, чтобы оставлять доступ открытым («публичным», public) только к тем атрибутам и методам, которые будут взаимодействовать с внешним кодом. Остальные атрибуты и функции, которые, например, просто (или сложно) обслуживают свой класс, не должны быть доступны извне.
В Python это реализовано с помощью добавления к имени атрибута или метода одинарного или двойного подчеркивания. Первое используется в расчете на более сознательных и внимательных граждан. Второе — более жесткий вариант для всех остальных. Что это означает? Перейдем к конкретике и возьмем из нашего примера атрибут name:
По заветам ООП, и в первом, и во втором случае нужно узнать, написаны ли специальные методы для получения значения ( getter ) и/или модификации ( setter ) интересующего вас скрытого атрибута. Если да, то используйте их.
Эти методы обычно выглядят примерно так:
Перепишем весь класс:
Создадим объект и покажем правильный доступ к атрибуту _name (для __name все будет аналогично), который теперь обозначен как скрытый:
Результат работы программы:
Оказывается, мы можем переименовать созданный объект. Здорово. Но что, если нам запрещено называть птиц Иванами?
Тогда программа будет работать по-другому:
Свойства
В остальном это обычные методы.
Код вызова извне тоже изменится:
Результат работы программы останется прежним:
Начать и закончить: наследование и полиморфизм
Про наследование и полиморфизм уже шла речь в начале статьи. Но тогда нельзя было приводить полноценные примеры на Python. Теперь, когда мы подтянули матчасть, написали и много раз переписали свой первый класс… как говорится, примеры в студию! Поэтому, если вам требуется время, чтобы вернуться в начало статьи и повторить эту тему — не волнуйтесь, я подожду.
Повторили? Тогда полетели дальше.
Наследование
Оттолкнемся от нашего класса Bird. Вспомним, как он выглядит:
Создадим новый класс Duck (утка), который максимально использует код класса Bird и допишем в него немного своего кода. То есть Duck будет наследником Bird, который является родителем, базовым классом. Это в принципе ожидаемо: чтобы некая сферическая птица в вакууме стала уткой, достаточно добавить пару опознавательных знаков (потому что для примера мы придумали простую модель). Итак, пусть у класса Duck будут такие динамические атрибуты:
Атрибуты «скорость полета» и «высота полета» бессмысленно помещать в базовый класс, потому что не все птицы умеют летать:
В строке class Duck (Bird) мы в скобках указываем имя класса, от которого хотим делать наследование.
В методе def __init__ (self, name, id, age, fly_speed, fly_height) мы, помимо заполнения значений атрибутов текущего объекта класса Duck, вызываем инициализатор объекта базового класса (Bird):
super().__init__(name, id, age)
Теперь проверим, как работает это ваше наследование. Как обычно, создаем объект, заполняем его данными и просим рассказать о себе:
Отметим, что внутри метода info() у утки (см. класс Duck) мы, по аналогии с инициализатором и чисто для полноты информации — вызываем свой метод info() у базового класса (то есть у Bird):
Ну вот, у нас получилась утка, которая может рассказать о себе и при этом помнит, что она Птица — не забывает свои корни. Жирным выделены атрибуты базового класса:
Идентификационный номер: 22
Скорость полета: 110
Отлично, с уткой все получилось удачно. Но означает ли это, что можно создавать еще много аналогичных классов-наследников, используя один и тот же код базового класса? А еще интереснее узнать вот что:
super().__init__(name, id, age)
Теперь проверим, как это сработает на этот раз. Жирным выделены атрибуты, относящиеся к базовому классу:
Идентификационный номер: 22
Вид: Галапагосский баклан
Количество особей: 60
Теперь сведем воедино работу объектов всех трех классов — родителя и наследников :
Создавать такие объекты можно в любом порядке. Как видите, вообще не обязательно, чтобы объект базового класса создавался первым.
Жирным выделены атрибуты, относящиеся к базовому классу:
Идентификационный номер: 22
Вид: Галапагосский баклан
Количество особей: 60
Идентификационный номер: 9
Идентификационный номер: 77
Скорость полета: 110
Мы показали, что метод info() действительно работает по-разному — в зависимости от класса того объекта, который обращается к этому методу.
Мы применили все знания, полученные в статье, и только в конце смогли написать код в соответствии с парадигмой полиморфизма.
Надеюсь, тот, кто дочитал это до конца, получил ответ на свой вопрос!