Python поддерживает три парадигмы программирования:
Основными концециями объектно-ориентированной парадигмы являются понятия объекта и класса.
Объект — это базовая сущность в Python для представления данных, обладающая определенным состоянием и поведением.
Класс — это способ описания множества однотипных объектов с одинаковыми свойствами и одинаковыми операциями для совершения действий над объектами этого множества.
Встроенные типы данных (int
, float
, complex
, bool
, str
, tuple
, list
, dict
, set
) являются встроенными классами в Python.
Пользовательский класс определяет НОВЫЙ ТИП ДАННЫХ, который не является встроенным типом в Python, и используется для создания объектов НОВОГО ТИПА. Объекты, созданные на основе класса, называются экземплярами класса (class instance).
Объектно-ориентированная парадигма — это программирование с применением экземпляров пользовательских классов.
С точки зрения программирования, класс — это способ группирования переменных, созданных явными присваиваниями, и функций с целью дальнейшего многократного их использования посредством создания экземпляров класса.
Классы группируют внутри себя переменные и функции, которые после создания экземпляра этого класса, становятся атрибутами экземпляра.
Атрибуты — это имена/переменные, которые связаны с объектом и используются для получения информации об объекте, а также для совершения действий над этим объектом.
Функциональные атрибуты объекта называются методами.
Нефункциональные атрибуты объектов класса и экземпляров класса будем в дальнейшем называть атрибутами.
Для создания класса используется оператор class
.
Оператор class
является составным оператором, тело которого состоит из операторов явного приcваивания =
и операторов определения функций def
.
Синтаксический шаблон оператора class
:
class <NameOfClass>(...):
<atribute1> = ...
...
<atributeN> = ...
def <method1>(...):
...
...
def <methodM>(...):
...
Рекомендовано начинать имя класса с буквы верхнего регистра.
При выполнении оператора class
осуществляется выполнение операторов из тела класса, создание объекта класса и неявное присваивание объекта класса переменной <Имя_класса>
.
class MyClass:
"""A simple example class"""
var1 = 1
var2 = "second"
def f1():
var3 = "local"
return "Method of MyClass"
Переменные и функции, определенные внутри оператора class
на верхнем уровне, становятся атрибутами и методами созданного объекта класса
MyClass, MyClass.var1, MyClass.var2, MyClass.f1()
(__main__.MyClass, 1, 'second', 'Method of MyClass')
Атрибуты объекта класса можно изменять за пределами оператора class
с использованием оператора присваивания
MyClass.var1 = 2; f'{MyClass.var1 = }'
'MyClass.var1 = 2'
Атрибуты объекта класса можно создавать за пределами оператора class
с использованием оператора присваивания для НОВОГО атрибута объекта класса
MyClass.var3 = "new"; f'{MyClass.var3 = }'
"MyClass.var3 = 'new'"
Просмотрим все атрибуты класса MyClass
, которые не начинаются с символа подчеркивания
[x for x in dir(MyClass) if not x.startswith("_")]
['f1', 'var1', 'var2', 'var3']
Строковый литерал строк документации записывается в атрибут __doc__
объекта класса при выполнении оператора class
MyClass.__doc__
'A simple example class'
С объектом класса в коде можно выполнить два действия: использовать атрибуты и методы объекта класса и создать экземпляры класса.
Для создания экземпляра класса необходимо обратиться к объекту класса как к функции
I1 = MyClass(); I2 = MyClass()
I1, I2
(<__main__.MyClass at 0x243f4cd28e0>, <__main__.MyClass at 0x243f4c42190>)
Объекты классов и объекты экземпляров классов являются объектами разных типов
type(MyClass), type(I1)
(type, __main__.MyClass)
Атрибуты и методы объекта класса становятся атрибутами и методами для КАЖДОГО экземпляра класса. Говорят, что атрибуты/методы класса наследуются всеми экземплярами, созданными на основе класса
I1.var1, I2.var2
(2, 'second')
Все экземпляры, созданные на основе класса, разделяют пространство имен этого класса. Это означает, что изменение значений атрибутов и методов объекта класса отражается на ВСЕХ экземплярах этого класса
MyClass.var1 = 10; I1.var1, I2.var1
(10, 10)
Присваивание нового значения для атрибута класса через объект экземпляра класса создает новую одноименную переменную в собственном пространстве имен конкретного экземпляра класса. Изменение значения атрибута не отражается ни на объекте класса, ни на других экземплярах этого класса
I1.var1 = 5; MyClass.var1, I1.var1, I2.var1
I1.var4 = 4
[x for x in dir(MyClass) if not x.startswith("_")]
['f1', 'var1', 'var2', 'var3']
С экземпляром класса в коде можно выполнить только одно действие: сослаться на атрибуты или методы, которые расположены в собственном пространстве имен экземпляра или которые унаследованы от класса.
Основным назначением методов класса является обработка экземпляров классов.
Для того, чтобы метод класса мог обрабатывать конкретный экземпляр класса, он/метод должен содержать специальный первый аргумент, который по соглашению называется self
, для автоматической передачи методу класса экземпляра класса, на котором был вызван метод.
class MyClass:
def f1(self, x, y):
return self, x, y
Аналогом self
в Python является this
в С++ и Java.
При вызове первый аргумент self
заполняется автоматически для ссылки на экземпляр, на котором произведен вызов, для дальнейшей обработки экземпляра класса. Значения аргументов, перечисленные в круглых скобках при вызове метода, сопоставляются со вторым и последующими аргументам метода класса.
I1 = MyClass(); I1, I1.f1(2,3)
(<__main__.MyClass at 0x243f4cd2f10>, (<__main__.MyClass at 0x243f4cd2f10>, 2, 3))
Аргумент self
метода класса связывает метод класса с экземпляром класса, на котором осуществляется вызов этого метода. Так как на основании одного класса может быть создано несколько экземпляров этого класса, каждый экземпляр класса должен обрабатываться одним и тем же методом уникально и независимо от других экземпляров класса.
Вызов экземпляр.метод(аргументы)
автоматически отображается на вызов метода класса класс.метод(экземпляр, аргументы)
, передавая экземпляр
в первом аргументе унаследованной функции метод
.
Объект метода класса для экземпляра класса, является не объектом функции, а объектом связанного метода. В объект связанного метода Python автоматически помещает экземпляр класса первым аргументом, связывая экземпляр класса и метод класса
MyClass.f1, I1.f1
(<function __main__.MyClass.f1(self, x, y)>, <bound method MyClass.f1 of <__main__.MyClass object at 0x00000243F4CD2F10>>)
Через атрибут __self__
объекта связанного метода можно получить доступ к экземпляру класса, на котором вызывается метод. Через атрибут __func__
объекта связанного метода можно получить доступ к объекту функции класса, который метод реализует.
bound_method = I1.f1
bound_method.__self__, bound_method.__func__
(<__main__.MyClass at 0x243f4cd2f10>, <function __main__.MyClass.f1(self, x, y)>)
Если действия над экземплярами класса реализуются при определении класса с помощью методов класса, то это называется концепцией инкапсуляции. Код для обработки экземпляров класса пишется только один раз в виде метода класса, метод класса наследуется экземплярами класса при их создании и может использоваться каждым экземпляром класса по необходимости.
Для создания экземпляра класса прежде всего нужно создать соответствующий класс, используя оператор class <Имя_класса>(…)
. При выполнении оператора class
будет создан объект класса и реализована ссылка переменной <Имя_класса>
на этот объект класса.
Далее для создания экземпляра класса следует вызвать объект класса как функцию, указывая <Имя_класса>
и аргументы вызова либо пустые круглые скобки.
Если при построении экземпляра класса необходимо инициализировать данные этого объекта, то оператор class
должен содержать метод __init__
(не всегда так бывает).
Метод __init__
инициализирует экземпляр класса: он обязан передать значения аргументов при вызове метода атрибутам объекта.
При этом в операторе def
метода __init__
первым аргументом должен быть указан специальный аргумент self
.
При вызове метода инициализации экземпляра класса __init__
первый аргумент self
автоматически становится ссылкой на объект созданного экземпляра. Значения аргументов, перечисленные в круглых скобках при вызове объекта класса как функции <Имя_класса>(<аргумент1>,...,<аргументN>)
, сопоставляются со вторым и последующими аргументам метода класса __init__(self, <аргумент1>,...,<аргументN>)
.
class MyClass:
def __init__(self, who):
self.name = who
I1 = MyClass('Inna'); I2 = MyClass('Oleg')
I1.name, I2.name
('Inna', 'Oleg')
Присваивание атрибутам аргумента self
создает атрибут экземпляра или изменяет значение существующего атрибута в экземпляре, но не в классе. Такие присваивания возможны только внутри методов классов.
Значения атрибутов для каждого экземпляра класса уникальные и не зависят от значений аналогичных атрибутов других экземпляров.
Анализируя код метода инициализации __init__
, можно узнать имена атрибутов для экземпляров класса.
class Complex:
def __init__(self, realpart=0, imagpart=0):
self.r, self.i = realpart, imagpart
def modulus(self):
return (self.r**2 + self.i**2)**(1/2)
Экземпляры класса Complex
будут иметь два атрибута r
и i
и один метод modulus
, унаследованный от класса Complex
C1, C2 = Complex(1, 2), Complex(3)
C1.r, C2.i, C1.modulus(), C2.modulus()
(1, 0, 2.23606797749979, 3.0)