Что такое объекты в python
Пять важных понятий, необходимых для быстрого старта в Python ООП
ООП расшифровывается как объектно-ориентированное программирование. Эта концепция представляет собой стиль решения задач программирования, когда свойства и поведение реального объекта упакованы в код как единое целое.
Этот стиль программирования обеспечивает модульность и масштабирование при наименьшем количестве проблем.
Недавно мы рассмотрели 30 вопросов с собеседований по теме ООП, а сегодня попробуем разобраться с этим немного глубже.
Python — это динамически типизированный интерпретируемый язык программирования высокого уровня. Python поддерживает несколько понятий ООП, включая следующие:
1. Классы в Python
Класс — это план реальной сущности. В Python он создается с использованием ключевого слова class, как показано в следующем фрагменте кода.
В приведенном выше примере:
2. Объекты в Python
Как только класс Person определен, вы можете использовать его для создания экземпляра, передавая значения, как показано ниже.
В приведенном выше примере:
3. Наследование в Python
Как следует из названия, это концепция о наследовании свойств от существующего объекта. Наследование увеличивает возможность повторного использования кода. Одиночное, множественное и многоуровневое наследование — это лишь немногие из большого числа типов, поддерживаемых Python.
В следующем примере показано, как использовать наследование в Python:
При множественном наследовании классы наследуются слева направо в круглых скобках, в зависимости от алгоритма Method Resolution Order (MRO).
4. Инкапсуляция в Python
Это концепция упаковки данных так, что внешний мир имеет доступ только к открытым свойствам. Некоторые свойства могут быть скрыты, чтобы уменьшить уязвимость. Это так называемая реализация сокрытия данных. Например, вы хотите купить брюки с интернет-сайта. Данные, которые вам нужны, это их стоимость и доступность. Количество предметов и их расположение — это информация, которая вас не беспокоит. Следовательно, эта информация скрыта.
В Python это реализуется путем создания private, protected и public переменных и методов экземпляра.
Private свойства имеют двойное подчеркивание (__) в начале, в то время как protected имеют одиночное подчеркивание (_). По умолчанию, все остальные переменные и методы являются public.
Private атрибуты доступны только внутри класса и недоступны для дочернего класса (если он унаследован). Protected доступны внутри класса, но доступны и дочернему классу. Все эти ограничения сняты для public атрибутов.
Следующие фрагменты кода являются примером этой концепции:
5. Полиморфизм в Python
Это концепция, при которой функция может принимать несколько форм в зависимости от количества аргументов или типа аргументов, переданных функции.
В приведенном выше примере ключевое слово super используется для вызова метода родительского класса. Оба класса имеют метод show_salary. В зависимости от типа объекта, который выполняет вызов этой функции, выходные данные различаются.
Python также имеет встроенные функции, работающие с полиморфизмом. Одним из самых простых примеров является функция print в Python.
В приведенном выше фрагменте кода:
Класс и объект в Python
Объектно-ориентированное программирование в Python
Python — это процедурно-ориентированный и одновременно объектно-ориентированный язык программирования.
Процедурно-ориентированный
«Процедурно-ориентированный» подразумевает наличие функций. Программист может создавать функции, которые затем используются в сторонних скриптах.
Объектно-ориентированный
«Объектно-ориентированный» подразумевает наличие классов. Есть возможность создавать классы, представляющие собой прототипы для будущих объектов.
Создание класса в Python
Синтаксис для написания нового класса:
Атрибут:
Атрибут — это элемент класса. Например, у прямоугольника таких 2: ширина ( width ) и высота ( height ).
Метод:
Конструктор:
Создание объекта с помощью класса Rectangle:
Что происходит при создании объекта с помощью класса?
При создании объекта класса Rectangle запускается конструктор выбранного класса, и атрибутам нового объекта передаются значения аргументов. Как на этом изображении:
Конструктор с аргументами по умолчанию
В других языках программирования конструкторов может быть несколько. В Python — только один. Но этот язык разрешает задавать значение по умолчанию.
Все требуемые аргументы нужно указывать до аргументов со значениями по умолчанию.
Сравнение объектов
В Python объект, созданный с помощью конструктора, занимает реальное место в памяти. Это значит, что у него есть точный адрес.
Атрибуты
В Python есть два похожих понятия, которые на самом деле отличаются:
Стоит разобрать на практике:
Атрибут
Объекты, созданные одним и тем же классом, будут занимать разные места в памяти, а их атрибуты с «одинаковыми именами» — ссылаться на разные адреса. Например:
Атрибуты функции
Обычно получать доступ к атрибутам объекта можно с помощью оператора «точка» (например, player1.name ). Но Python умеет делать это и с помощью функции.
Функция | Описание |
---|---|
getattr (obj, name[,default]) | Возвращает значение атрибута или значение по умолчанию, если первое не было указано |
hasattr (obj, name) | Проверяет атрибут объекта — был ли он передан аргументом «name» |
setattr (obj, name, value) | Задает значение атрибута. Если атрибута не существует, создает его |
delattr (obj, name) | Удаляет атрибут |
Встроенные атрибуты класса
Объекты класса — дочерние элементы по отношению к атрибутам самого языка Python. Таким образом они заимствуют некоторые атрибуты:
Переменные класса
Переменные класса в Python — это то же самое, что Field в других языках, таких как Java или С#. Получить к ним доступ можно только с помощью имени класса или объекта.
Для получения доступа к переменной класса лучше все-таки использовать имя класса, а не объект. Это поможет не путать «переменную класса» и атрибуты.
У каждой переменной класса есть свой адрес в памяти. И он доступен всем объектам класса.
Составляющие класса или объекта
Python. Урок 14. Классы и объекты
Данный урок посвящен объектно-ориентированному программированию в Python. Разобраны такие темы как создание объектов и классов, работа с конструктором, наследование и полиморфизм в Python.
Основные понятия объектно-ориентированного программирования
Объектно-ориентированное программирование (ООП) является методологией разработки программного обеспечения, в основе которой лежит понятие класса и объекта, при этом сама программа создается как некоторая совокупность объектов, которые взаимодействую друг с другом и с внешним миром. Каждый объект является экземпляром некоторого класса. Классы образуют иерархии. Более подробно о понятии ООП можно прочитать на википедии.
Выделяют три основных “столпа” ООП- это инкапсуляция, наследование и полиморфизм.
Инкапсуляция
Наследование
Под наследованием понимается возможность создания нового класса на базе существующего. Наследование предполагает наличие отношения “является” между классом наследником и классом родителем. При этом класс потомок будет содержать те же атрибуты и методы, что и базовый класс, но при этом его можно (и нужно) расширять через добавление новых методов и атрибутов.
Примером базового класса, демонстрирующего наследование, можно определить класс “автомобиль”, имеющий атрибуты: масса, мощность двигателя, объем топливного бака и методы: завести и заглушить. У такого класса может быть потомок – “грузовой автомобиль”, он будет содержать те же атрибуты и методы, что и класс “автомобиль”, и дополнительные свойства: количество осей, мощность компрессора и т.п..
Полиморфизм
Полиморфизм позволяет одинаково обращаться с объектами, имеющими однотипный интерфейс, независимо от внутренней реализации объекта. Например, с объектом класса “грузовой автомобиль” можно производить те же операции, что и с объектом класса “автомобиль”, т.к. первый является наследником второго, при этом обратное утверждение неверно (во всяком случае не всегда). Другими словами полиморфизм предполагает разную реализацию методов с одинаковыми именами. Это очень полезно при наследовании, когда в классе наследнике можно переопределить методы класса родителя.
Классы в Python
Создание классов и объектов
Создание класса в Python начинается с инструкции class. Вот так будет выглядеть минимальный класс.
Класс состоит из объявления (инструкция class), имени класса (нашем случае это имя C) и тела класса, которое содержит атрибуты и методы (в нашем минимальном классе есть только одна инструкция pass).
Для того чтобы создать объект класса необходимо воспользоваться следующим синтаксисом:
имя_объекта = имя_класса()
Статические и динамические атрибуты класса
Как уже было сказано выше, класс может содержать атрибуты и методы. Атрибут может быть статическим и динамическим (уровня объекта класса). Суть в том, что для работы со статическим атрибутом, вам не нужно создавать экземпляр класса, а для работы с динамическим – нужно. Пример:
В представленном выше классе, атрибут default_color – это статический атрибут, и доступ к нему, как было сказано выше, можно получить не создавая объект класса Rectangle.
width и height – это динамические атрибуты, при их создании было использовано ключевое слово self. Пока просто примите это как должное, более подробно про self будет рассказано ниже. Для доступа к width и height предварительно нужно создать объект класса Rectangle:
Если обратиться через класс, то получим ошибку:
При этом, если вы обратитесь к статическому атрибуту через экземпляр класса, то все будет ОК, до тех пор, пока вы не попытаетесь его поменять.
Проверим ещё раз значение атрибута default_color:
Присвоим ему новое значение:
Создадим два объекта класса Rectangle и проверим, что default_color у них совпадает:
Если поменять значение default_color через имя класса Rectangle, то все будет ожидаемо: у объектов r1 и r2 это значение изменится, но если поменять его через экземпляр класса, то у экземпляра будет создан атрибут с таким же именем как статический, а доступ к последнему будет потерян:
Меняем default_color через r1:
При этом у r2 остается значение статического атрибута:
Вообще напрямую работать с атрибутами – не очень хорошая идея, лучше для этого использовать свойства.
Методы класса
Добавим к нашему классу метод. Метод – это функция, находящаяся внутри класса и выполняющая определенную работу.
Методы бывают статическими, классовыми (среднее между статическими и обычными) и уровня класса (будем их называть просто словом метод). Статический метод создается с декоратором @staticmethod, классовый – с декоратором @classmethod, первым аргументом в него передается cls, обычный метод создается без специального декоратора, ему первым аргументом передается self:
Статический и классовый метод можно вызвать, не создавая экземпляр класса, для вызова ex_method() нужен объект:
Конструктор класса и инициализация экземпляра класса
В Python разделяют конструктор класса и метод для инициализации экземпляра класса. Конструктор класса это метод __new__(cls, *args, **kwargs) для инициализации экземпляра класса используется метод __init__(self). При этом, как вы могли заметить __new__ – это классовый метод, а __init__ таким не является. Метод __new__ редко переопределяется, чаще используется реализация от базового класса object (см. раздел Наследование), __init__ же наоборот является очень удобным способом задать параметры объекта при его создании.
Создадим реализацию класса Rectangle с измененным конструктором и инициализатором, через который задается ширина и высота прямоугольника:
Что такое self?
До этого момента вы уже успели познакомиться с ключевым словом self. self – это ссылка на текущий экземпляр класса, в таких языках как Java, C# аналогом является ключевое слово this. Через self вы получаете доступ к атрибутам и методам класса внутри него:
В приведенной реализации метод area получает доступ к атрибутам width и height для расчета площади. Если бы в качестве первого параметра не было указано self, то при попытке вызвать area программа была бы остановлена с ошибкой.
Уровни доступа атрибута и метода
Если вы знакомы с языками программирования Java, C#, C++ то, наверное, уже задались вопросом: “а как управлять уровнем доступа?”. В перечисленных языка вы можете явно указать для переменной, что доступ к ней снаружи класса запрещен, это делается с помощью ключевых слов (private, protected и т.д.). В Python таких возможностей нет, и любой может обратиться к атрибутам и методам вашего класса, если возникнет такая необходимость. Это существенный недостаток этого языка, т.к. нарушается один из ключевых принципов ООП – инкапсуляция. Хорошим тоном считается, что для чтения/изменения какого-то атрибута должны использоваться специальные методы, которые называются getter/setter, их можно реализовать, но ничего не помешает изменить атрибут напрямую. При этом есть соглашение, что метод или атрибут, который начинается с нижнего подчеркивания, является скрытым, и снаружи класса трогать его не нужно (хотя сделать это можно).
Внесем соответствующие изменения в класс Rectangle:
В приведенном примере для доступа к _width и _height используются специальные методы, но ничего не мешает вам обратиться к ним (атрибутам) напрямую.
Если же атрибут или метод начинается с двух подчеркиваний, то тут напрямую вы к нему уже не обратитесь (простым образом). Модифицируем наш класс Rectangle:
Попытка обратиться к __width напрямую вызовет ошибку, нужно работать только через get_width():
Но на самом деле это сделать можно, просто этот атрибут теперь для внешнего использования носит название: _Rectangle__width:
Свойства
Свойством называется такой метод класса, работа с которым подобна работе с атрибутом. Для объявления метода свойством необходимо использовать декоратор @property.
Важным преимуществом работы через свойства является то, что вы можете осуществлять проверку входных значений, перед тем как присвоить их атрибутам.
Сделаем реализацию класса Rectangle с использованием свойств:
Теперь работать с width и height можно так, как будто они являются атрибутами:
Можно не только читать, но и задавать новые значения свойствам:
Если вы обратили внимание: в setter’ах этих свойств осуществляется проверка входных значений, если значение меньше нуля, то будет выброшено исключение ValueError:
Наследование
В организации наследования участвуют как минимум два класса: класс родитель и класс потомок. При этом возможно множественное наследование, в этом случае у класса потомка может быть несколько родителей. Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать. По умолчанию все классы в Python являются наследниками от object, явно этот факт указывать не нужно.
Синтаксически создание класса с указанием его родителя выглядит так:
class имя_класса(имя_родителя1, [имя_родителя2,…, имя_родителя_n])
Переработаем наш пример так, чтобы в нем присутствовало наследование:
Родительским классом является Figure, который при инициализации принимает цвет фигуры и предоставляет его через свойства. Rectangle – класс наследник от Figure. Обратите внимание на его метод __init__: в нем первым делом вызывается конструктор (хотя это не совсем верно, но будем говорить так) его родительского класса:
super – это ключевое слово, которое используется для обращения к родительскому классу.
Теперь у объекта класса Rectangle помимо уже знакомых свойств width и height появилось свойство color:
Полиморфизм
Как уже было сказано во введении в рамках ООП полиморфизм, как правило, используется с позиции переопределения методов базового класса в классе наследнике. Проще всего это рассмотреть на примере. Добавим в наш базовый класс метод info(), который печатает сводную информацию по объекту класса Figure и переопределим этот метод в классе Rectangle, добавим в него дополнительные данные:
Посмотрим, как это работает
Таким образом, класс наследник может расширять функционал класса родителя.
P.S.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Python. Урок 14. Классы и объекты : 18 комментариев
А вот если Вы добавите вот это
.entry-title a:last-child <
float:right;
>
в свой css будет намного удобнее, нежели вы будите использовать 2-ную табуляцию в HTML. Спасибо.
Класс, о методе super() вообще ни слова
Спасибо за замечание! Добавим!
Про self ничего не сказано. Похоже на ссылку на текущий обьект.
Да, это действительно ссылка на текущий объект. Нужно будет вообще этот урок переработать, в нем плохо раскрыты многие вопросы! Спасибо за комментарий!
О методе __new__(cls) тоже нет ни слова, а он так же участвует в конструировании экземпляра класса.
ОК, спасибо! Добавим!
Наконец-то всё стало понятно. Огромное спасибо за разъяснение на уровне 1 класса 2 четверти!
Определение инкапсуляции неверное. Приведенное определение скорее присуще самому понятию “класс”. А инкапсуляция – это сокрытие деталей реализации.
> Атрибут может быть статическим и не статическим (уровня объекта класса)
В других языках принято “не статические атрибуты” называть динамическими. Предлагаю использовать, чтобы язык не ломать 🙂
Пытаюсь разобраться с декораторами.
@property
def width(self):
return self.__width
@width.setter
def width(self, w):
if w > 0:
self.__width = w
else:
raise ValueError
Понял назначение методов уровня Класс. Но не понятно назначение классовых и статических методов (@classmethod, @staticmethod)
Столкнулся с проблемой
есть класс
class Users(): #класс списка пользователей
def __init__(self):
self.item=[]
self.num=0
есть класс пользователя
class Aduser(): #класс пользователь домена
def __init__(self):
self.fio=”” # ФИО
self.login=”” # login
self.email=”” # e-mail
self.list=[] # принадлежность к спискам
self.spec=”” # должность
self.dept=”” # отдел
self.stage=True # состояние активности учетной записи
self.desc=”” # примечание
usrs = Users()
usr = Aduser()
…
не мону понять почему не срабатывает конструкция
usrs.item.append[usr]
точнее срабатывает но в usrs.item[] приходит пустой объект Adusers()
Ссылки на предыдущие уроки не нашел, причем тут декораторы и вообще, что это (хотя бы ссылкой) тоже не нашел.
Работаю с питоном уже больше года. Долго пытался понять что такое @property и @setter, А тут автор за 10 строчек объяснил, браво!
Класно описано. Только вот про сеттеры ни слова объяснения, из кода приходится догадыватся.
Классы и объекты в Python
Классы и объекты в Python являются основными строительными блоками языка программирования Python.
Классы в Python
Как было бы, если бы вы могли объявить тип данных, который сам содержит более одного типа данных и может работать с ними с помощью любой функции? Класс в Python дает вам такую возможность.
Класс Python – это план, по которому создаются экземпляры класса.
Определение класса
Вот самая базовая структура определения класса Python.
Теперь поработаем с реальными примерами.
Этот пример не требует пояснений. Как мы знаем, строки, начинающиеся с символа «#», представляют собой комментарии в Python. Они объясняют исполняемые шаги, а код дает следующий результат.
Определение класса в Python:
Эта строка отмечает начало определения класса для класса «Person».
Переменные класса
«name» и «age» – две переменные-члены класса «Person». Каждый раз, когда мы объявляем объект этого класса, он будет содержать эти две переменные в качестве своих членов. Эта часть не является обязательной, так как они могут быть инициализированы конструктором.
Конструктор классов
Конструктор класса в Python – это первый фрагмент кода, который должен выполняться при создании нового объекта класса.
Прежде всего, конструктор можно использовать для помещения значений в переменные-члены. Вы также можете распечатать сообщения в конструкторе, чтобы убедиться, что объект был создан.
Метод конструктора начинается с def __init__. После этого первым параметром должно быть значение «self», так как он передает ссылку на экземпляр самого класса. Вы также можете добавить дополнительные параметры, как показано в примере. «personName» и «personAge» – это два параметра, которые необходимо отправить, когда должен быть создан новый объект.
Методы класса
Методы объявляются следующим образом:
В предустановленном примере мы видели, что метод showName() печатает значение «name» этого объекта.
Создание объектов
Создание объектов в Python довольно простое. Сначала вы указываете имя нового объекта, за которым следует оператор присваивания и имя класса с параметрами (как определено в конструкторе).
Помните, что количество и тип параметров должны быть совместимы с параметрами, полученными в функции-конструкторе.
Когда объект создан, могут быть вызваны методы-члены и доступны атрибуты-члены (при условии, что они доступны).
Заметки об объектной системе языка Python ч.1
Несколько заметок об объектной системе python’a. Рассчитаны на тех, кто уже умеет программировать на python. Речь идет только о новых классах (new-style classes) в python 2.3 и выше. В этой статье рассказывается, что такое объекты и как происходит поиск атрибутов.
Объекты
У a тоже есть __dict__ и __class__:
Класс и тип — это одно и то же.
a.__dict__ — это словарь, в котором находятся внутренние (или специфичные для объекта) атрибуты, в данном случае ‘name’. А в a.__class__ класс (тип).
И, например, в методах класса присваивание self.foo = bar практически идентично self.__dict__[‘foo’] = bar или сводится к аналогичному вызову.
В __dict__ объекта нет методов класса, дескрипторов, классовых переменных, свойств, статических методов класса, все они определяются динамически с помощью класса из __class__ атрибута, и являются специфичными именно для класса (типа) объекта, а не для самого объекта.
Пример. Переопределим класс объекта a:
Смотрим, что поменялось.
Значение a.name осталось прежним, т.е. __init__ не вызывался при смене класса.
Работа с атрибутам объекта: установка, удаление и поиск, равносильна вызову встроенных функций settattr, delattr, getattr:
a.x = 1 setattr(a, ‘x’, 1)
del a.x delattr(a, ‘x’)
a.x getattr(a, ‘x’)
При этом стоит стоит понимать, что setattr и delattr влияют и изменяют только сам объект (точнее a.__dict__), и не изменяют класс объекта.
qux — является классовой переменной, т.е. она «принадлежит» классу B, а не объекту a:
Если мы попытаемся удалить этот атрибут, то получим ошибку, т.к. delattr будет пытаться удалить атрибут из a.__dict__
Далее, если мы попытаемся изменить (установить) атрибут, setattr поместит его в __dict__, специфичный для данного, конкретного объекта.
Ну и раз есть ‘qux’ в __dict__ объекта, его можно удалить с помощью delattr:
После удаления, a.qux будет возвращать значение классовой переменной:
Объекты и классы
Классы — это объекты, и у них тоже есть специальные атрибуты __class__ и __dict__.
>>> class A ( object ):
. pass
.
Правда __dict__ у классов не совсем словарь
Но __dict__ ответственен за доступ к внутреннему пространству имен, в котором хранятся методы, дескрипторы, переменные, свойства и прочее:
В классах помимо __class__ и __dict__, имеется еще несколько специальных атрибутов: __bases__ — список прямых родителей, __name__ — имя класса. [1]
Классы можно считать эдакими расширениями обычных объектов, которые реализуют интерфейс типа. Множество всех классов (или типов) принадлежат множеству всех объектов, а точнее является его подмножеством. Иначе говоря, любой класс является объектом, но не всякий объект является классом. Договоримся называть обычными объектами(regular objects) те объекты, которые классами не являются.
Небольшая демонстрация, которая станет лучше понятна чуть позже.
Класс является объектом.
>>> class A ( object ):
. pass
.
>>> isinstance (A, object )
True
Число — это тоже объект.
Класс — это класс (т.е. тип).
>>> isinstance (A, type )
True
А вот число классом (типом) не является. (Что такое type будет пояснено позже)
Ну и a — тоже обычный объект.
>>> a = A()
>>> isinstance (a, A)
True
>>> isinstance (a, object )
True
>>> isinstance (a, type )
False
И у A всего один прямой родительский класс — object.
Часть специальных параметров можно даже менять:
С помощью getattr получаем доступ к атрибутам класса:
Поиск атрибутов в обычном объекте
В первом приближении алгоритм поиска выглядит так: сначала ищется в __dict__ объекта, потом идет поиск по __dict__ словарям класса объекта (который определяется с помощью __class__) и __dict__ его базовых классов в рекурсивном порядке.
Т.к. в обычных объектах a и b нет в __dict__ атрибута ‘qux’, то поиск продолжается во внутреннем словаре __dict__ их типа (класса), а потом по __dict__ словарям родителей в определенном порядке:
Меняем атрибут qux у класса A. И соответственно должны поменяться значения, которые возвращают экземпляры класса A — a и b:
Точно так же в рантайме к классу можно добавить метод:
И доступ к нему появится у экземпляров:
Точно так же как и с любыми другими объектами, можно удалить атрибут класса, например, классовую переменную qux:
Она удалиться из __dict__
И доступ у экземляров пропадет.
У классов почти такой же поиск атрибутов, как и у обычных объектов, но есть отличия: поиск начинается с собственного __dict__ словаря, а потом идет поиск по __dict__ словарям суперклассов (которые хранятся в __bases__) по опредленному алгоритму, а затем по классу в __class__ и его суперклассах. (Подробнее об этом позже).
Cсылки
Примечания
[1] О __module__ и __doc__ для простоты изложения пока забудем. Полный список атрибутов класса можно посмотреть в документации