Объектно-ориентированное Программирование в Python
Объектно-ориентированное программирование (ООП) — это парадигма программирования, где различные компоненты компьютерной программы моделируются на основе реальных объектов. Объект — это что-либо, у чего есть какие-либо характеристики и то, что может выполнить какую-либо функцию.
Представьте сценарий, где вам нужно разработать болид Формулы-1 используя подход объектно-ориентированного программирования. Первое, что вам нужно сделать — это определить реальные объекты в настоящей гонке Формула-1. Какие аспекты в Формуле-1 обладают определенными характеристиками и могут выполнять ту или иную функцию?
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Telegram Чат & Канал
Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Один из очевидных ответов на этот вопрос — гоночный болид. Условный болид может обладать такими характеристиками как:
Соответственно, болид можно запустить, остановить, ускорить, и так далее. Гонщик может быть еще одним объектом в Формуле-1. Гонщик имеет национальность, возраст, пол, и так далее, кроме этого, он обладает таким функционалом, как управление болидом, рулевое управление, переключение передач.
Как и в этом примере, в объектно-ориентированном программировании мы создадим объекты, которые будут соответствовать реальным аспектам.
Стоит обратить внимание на то, что объектно-ориентированное программирование — не зависящая от языка программирования концепция. Это общая концепция программирования и большинство современных языков, такие как Java, C#, C++ и Python поддерживают объектно-ориентированное программирование.
В этой статье мы разберем подробную инструкцию объектно-ориентированного программирования в Python, но перед этим, рассмотрим некоторые преимущества и недостатки объектно-ориентированного программирования.
Преимущества и недостатки ООП Python
Рассмотрим несколько основных преимуществ объектно-ориентированного программирования:
- Объектно-ориентированное программирование подразумевает повторное использование. Компьютерная программа написанная в форме объектов и классов может быть использована снова в других проектах без повторения кода;
- Использование модулярного подхода в объектно-ориентированном программировании позволяет получить читаемый и гибкий код;
- В объектно-ориентированном программировании каждый класс имеет определенную задачу. Если ошибка возникнет в одной части кода, вы можете исправить ее локально, без необходимости вмешиваться в другие части кода;
- Инкапсуляция данных (которую мы рассмотрим дальше в статье) вносит дополнительный уровень безопасности в разрабатываемую программу с использованием объектно-ориентированного подхода;
Хотя объектно-ориентированное программирование обладает рядом преимуществ, оно также содержит определенные недостатки, некоторые из них находятся в списке ниже:
- Для создания объектов необходимо иметь подробное представление о разрабатываемом программном обеспечении;
- Не каждый аспект программного обеспечения является лучшим решением для реализации в качестве объекта. Для новичков может быть тяжело прочертить линию в золотой середине;
- С тем, как вы вносите все новые и новые классы в код, размер и сложность программы растет в геометрической прогрессии;
В следующем разделе мы рассмотрим ряд самых важных концепций объектно-ориентированного программирования.
Как и следует из названия, объектно-ориентированное программирование — это речь об объектах. Однако, перед тем как создать объект, нам нужно определить его класс.
Класс
Класс в объектно-ориентированном программировании выступает в роли чертежа для объекта. Класс можно рассматривать как карту дома. Вы можете понять, как выглядит дом, просто взглянув на его карту.
Cам по себе класс не представляет ничего. К примеру, нельзя сказать что карта является домом, она только объясняет как настоящий дом должен выглядеть.
Отношение между классом и объектом можно представить более наглядно, взглянув на отношение между машиной и Audi. Да, Audi – это машина. Однако, нет такой вещи, как просто машина. Машина — это абстрактная концепция, которую также реализуют в Toyota, Honda, Ferrari, и других компаниях.
Ключевое слово class используется для создания класса в Python. Название класса следует за ключом class , за которым следует двоеточие. Тело класса начинается с новой строки, с отступом на одну вкладку влево.
Давайте рассмотрим, как мы можем создать самый простой класс в Python. Взглянем на следующий код:
Заметки об объектной системе языка Python ч.1
Несколько заметок об объектной системе python’a. Рассчитаны на тех, кто уже умеет программировать на python. Речь идет только о новых классах (new-style classes) в python 2.3 и выше. В этой статье рассказывается, что такое объекты и как происходит поиск атрибутов.
Объекты
- __class__ — определяет класс или тип, экзмепляром которого является объект. Тип (или класс объекта) определяет его поведение; он есть у всех объектов, в том числе и встроенных. Тип и класс — это разные названия одного и того же. x.__class__ type(x).
- __dict__ словарь, дающий доступ к внутреннему пространству имен, он есть почти у всех объектов, у многих встроенных типов его нет.
>>> def foo (): pass
.
>>> foo . __class__
>>> foo . __dict__
<>
>>> ( 42 ) . __dict__
Traceback (most recent call last):
File «», line 1, in
AttributeError: ‘int’ object has no attribute ‘__dict__’
>>> ( 42 ) . __class__
>>> class A ( object ):
. qux = ‘A’
. def __init__ ( self , name):
. self . name = name
. def foo ( self ):
. print ‘foo’
.
>>> a = A( ‘a’ )
У a тоже есть __dict__ и __class__:
>>> a . __dict__
>>> a . __class__
>>> type (a)
>>> a . __class__ is type (a)
True
Класс и тип — это одно и то же.
>>> a . __class__ is type (a) is A
True
a.__dict__ — это словарь, в котором находятся внутренние (или специфичные для объекта) атрибуты, в данном случае ‘name’. А в a.__class__ класс (тип).
И, например, в методах класса присваивание self.foo = bar практически идентично self.__dict__[‘foo’] = bar или сводится к аналогичному вызову.
В __dict__ объекта нет методов класса, дескрипторов, классовых переменных, свойств, статических методов класса, все они определяются динамически с помощью класса из __class__ атрибута, и являются специфичными именно для класса (типа) объекта, а не для самого объекта.
Пример. Переопределим класс объекта a:
>>> class B ( object ):
. qux = ‘B’
. def __init__ ( self ):
. self . name = ‘B object’
. def bar ( self ):
. print ‘bar’
.
>>> a . __dict__
>>> a . foo()
foo
>>> a . __class__
>>> a . __class__ = B
>>> a . __class__
Значение a.name осталось прежним, т.е. __init__ не вызывался при смене класса.
>>> a . __dict__
Доступ к классовым переменным и методам «прошлого» класса A пропал:
>>> a . foo()
Traceback (most recent call last):
File «», line 1, in
AttributeError: ‘B’ object has no attribute ‘foo’
А вот классовые переменные и методы класса B доступы:
>>> a . bar()
bar
>>> a . qux
‘B’
Работа с атрибутам объекта: установка, удаление и поиск, равносильна вызову встроенных функций 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:
>>> a . qux
‘B’
>>> a . __dict__
Если мы попытаемся удалить этот атрибут, то получим ошибку, т.к. delattr будет пытаться удалить атрибут из a.__dict__
>>> delattr (a, ‘qux’ )
Traceback (most recent call last):
File «», line 1, in
AttributeError: qux
>>> del a . qux
Traceback (most recent call last):
File «», line 1, in
AttributeError: qux
>>> a . qux
‘B’
>>>
Далее, если мы попытаемся изменить (установить) атрибут, setattr поместит его в __dict__, специфичный для данного, конкретного объекта.
>>> b = B()
>>> b . qux
‘B’
>>> a . qux = ‘myB’
>>> a . qux
‘myB’
>>> a . __dict__
>>> b . qux
‘B’
>>>
Ну и раз есть ‘qux’ в __dict__ объекта, его можно удалить с помощью delattr:
>>> del a . qux
После удаления, a.qux будет возвращать значение классовой переменной:
>>> a . qux
‘B’
>>> a . __dict__
- класс для объекта — это значение специального атрибута __class__ и его можно менять. (Хотя в официальной документации говорится, что никаких гарантий нет, но на самом деле можно)
- почти каждый объект имеет свое пространство имен (атрибутов), доступ (не всегда полный), к которому осуществляется с помощью специального атрибута __dict__
- класс фактичеки влияет только на поиск атрибутов, которых нет в __dict__, как-то: методы класса, дескрипторы, магические методы, классовые переменные и прочее.
Объекты и классы
Классы — это объекты, и у них тоже есть специальные атрибуты __class__ и __dict__.
>>> class A ( object ):
. pass
.
>>> A . __class__
Правда __dict__ у классов не совсем словарь
>>> A . __dict__
Но __dict__ ответственен за доступ к внутреннему пространству имен, в котором хранятся методы, дескрипторы, переменные, свойства и прочее:
В классах помимо __class__ и __dict__, имеется еще несколько специальных атрибутов: __bases__ — список прямых родителей, __name__ — имя класса. [1]
Классы можно считать эдакими расширениями обычных объектов, которые реализуют интерфейс типа. Множество всех классов (или типов) принадлежат множеству всех объектов, а точнее является его подмножеством. Иначе говоря, любой класс является объектом, но не всякий объект является классом. Договоримся называть обычными объектами(regular objects) те объекты, которые классами не являются.
Небольшая демонстрация, которая станет лучше понятна чуть позже.
Класс является объектом.
>>> class A ( object ):
. pass
.
>>> isinstance (A, object )
True
>>> isinstance ( 42 , object )
True
>>> isinstance (A, type )
True
А вот число классом (типом) не является. (Что такое type будет пояснено позже)
>>> isinstance ( 42 , type )
False
>>>
Ну и a — тоже обычный объект.
>>> a = A()
>>> isinstance (a, A)
True
>>> isinstance (a, object )
True
>>> isinstance (a, type )
False
И у A всего один прямой родительский класс — object.
Часть специальных параметров можно даже менять:
>>> A . __name__
‘A’
>>> A . __name__ = ‘B’
>>> A
С помощью getattr получаем доступ к атрибутам класса:
>>> A . qux
‘A’
>>> A . foo
>>>
Поиск атрибутов в обычном объекте
В первом приближении алгоритм поиска выглядит так: сначала ищется в __dict__ объекта, потом идет поиск по __dict__ словарям класса объекта (который определяется с помощью __class__) и __dict__ его базовых классов в рекурсивном порядке.
>>> class A ( object ):
. qux = ‘A’
. def __init__ ( self , name):
. self . name = name
. def foo ( self ):
. print ‘foo’
.
>>> a = A()
>>> b = A()
Т.к. в обычных объектах a и b нет в __dict__ атрибута ‘qux’, то поиск продолжается во внутреннем словаре __dict__ их типа (класса), а потом по __dict__ словарям родителей в определенном порядке:
>>> b . qux
‘A’
>>> A . qux
‘A’
Меняем атрибут qux у класса A. И соответственно должны поменяться значения, которые возвращают экземпляры класса A — a и b:
>>> A . qux = ‘B’
>>> a . qux
‘B’
>>> b . qux
‘B’
>>>
Точно так же в рантайме к классу можно добавить метод:
>>> A . quux = lambda self : ‘i have quux method’
>>> A . __dict__[ ‘quux’ ]
at 0x7f7797a25b90>
>>> A . quux
>
И доступ к нему появится у экземпляров:
>>> a . quux()
‘i have quux method’
Точно так же как и с любыми другими объектами, можно удалить атрибут класса, например, классовую переменную qux:
>>> del A . qux
Она удалиться из __dict__
>>> A . __dict__[ ‘qux’ ]
Traceback (most recent call last):
File «», line 1, in
KeyError: ‘qux’
И доступ у экземляров пропадет.
>>> a . qux
Traceback (most recent call last):
File «», line 1, in
AttributeError: ‘A’ object has no attribute ‘qux’
>>>
У классов почти такой же поиск атрибутов, как и у обычных объектов, но есть отличия: поиск начинается с собственного __dict__ словаря, а потом идет поиск по __dict__ словарям суперклассов (которые хранятся в __bases__) по опредленному алгоритму, а затем по классу в __class__ и его суперклассах. (Подробнее об этом позже).
Cсылки
- Unifying types and classes in Python — главный документ, объясняющий что, как и зачем в новых классах.
- Making Types Look More Like Classes — PEP 252, описывающий отличие старых классов от новых.
- Built-in functions — детальное описание работы всех встроенных функций.
- Data model — детальное описание модели данных python’а.
- Python types and objects — объяснение объектной модели python на простых примерах с картинками.