Содержание
Введение
Прежде, чем говорить о классах – было бы хорошо понимать основы пространства имён в Python и основы жизненного цикла данных в нём.
Имена
Для начала – поговорим об именах.
Имя – это просто идентификатор, который указывает на какой-то объект в памяти.
Т.е., создавая объект в памяти – вы можете создать указатель, после чего обратиться к нему либо по имени – либо напрямую:
>>> i = 1 >>> id(i) 35049816 >>> id(1) 35049816
Имена могут указывать и на функции, которые так же являются объектами (как и всё в Python):
>>> def a(): ... print 'a' ... >>> i = a() a >>> id(a()) a 7881056 >>> id(i) 7881056
Пространства имён
Теперь рассмотрим понятие пространства имён (или namespace).
Грубо говоря, пространство имён – это не более чем коллекция нескольких имён (или “указателей” на объекты). Таких коллекций одновременно может существовать несколько, и каждая из них будет независима друг от друга. По сути – когда вы запускаете интерпретатор, и создаёте в нём функцию – вы уже используете как минимум два таких пространства, область видимости встроенных имён, которая создаётся при запуске интерпретатора. Именно поэтому нам сразу же доступны для использования встроенные переменные типа True
или False
и встроенные функции, таки как id()
, print()
и т.д. В глобальной области видимости – вы можете создавать свои объекты, такие как функции. А внутри функций – будет располагаться локальная область видимости самой функции.
Пространство имён хорошо проиллюстрировано на этой картинке:
Именно благодаря тому, что имеется несколько изолированных областей видимости – вы можете использовать одни и те же имена в различных пространствах имён. Например:
>>> def a(): ... a = 1 ... print a ... >>> def b(): ... a = 2 ... print a ... >>> a() 1 >>> b() 2
Области видимости
Области видимости Python – ещё одно необходимое условие для понимая работы классов и принципов ООП.
Область видимости – это часть кода, из которой доступ к пространству имён может быть получен без каких-либо префиксов. В каждый момент времени у вас есть как минимум три вложенных области видимости:
- область видимости текущей функции, которая содержит её локальные переменные;
- область видимости модуля, который содержит глобальные имена (такие как имена функций в нём);
- внешняя область видимости с встроенными именами.
Когда происходит обращение к переменной внутри функции – сначала выполняется её поиск в локальной области видимости внутри функции, затем – в области видимости модуля, и последним – во встроенной области видимости.
Возвращаясь к классам – они представляют собой такое же изолированное пространство имён и свою область видимости, как и функции или глобальная область видимости. Вы можете представлять себе класс как отдельную область видимости со своим пространством имён, которая содержит свой набор вложенных пространств имён (функции, или методы – мы рассмотрим понятие методы позже).
Лучше всего об этом рассказано в книге Марк Лутц – “Изучаем Python”, глава 16.
Создание класса
Если функция создаётся с помощью ключевого слова def
– то класс создаётся с помощью ключевого слова class
.
Вот пример простейшего класса:
>>> class ThisIsClass: ... pass
class
– указание на то, что далее последует объявление класса;ThisIsClass
– имя класса;pass
– оператор в классе.
Следует запомнить, что объект (или инстанс – instance) класса, и экземпляр класса – это две разные сущности.
Объект класса создаётся во время его объявления (грубо говоря – когда интерпретатор встречает слово class
в коде).
Экземпляр класса – отдельный, новый объект класса, который создаётся во время присваивания класса какой-то переменной и вызова с оператором ()
.
Например:
>>> id(ThisIsClass) // это объект класса 140249307055928 >>> cl = ThisIsClass() // экземпляр класса, новый объект в памяти >>> id(cl) 140249307072992 >>> cl = ThisIsClass // указатель на объект класса, но не экземпляр >>> id(cl) 140249307055928
После создания экземпляра класса – вы можете работать с ним, как с обычной переменной или функцией.
Атрибуты классов
Каждый класс может содержать свой набор атрибутов (переменных, определённых в классе).
Например:
>>> class ThisIsClass: ... attribute = 'This is attribute value'
Для получения доступа к атрибутам класса – используйте точку в качестве разделителя:
>>> ThisIsClass.attribute 'This is attribute value' >>> cl = ThisIsClass() >>> cl.attribute 'This is attribute value'
Главная идея в использовании классов, и вообще ООП – это многократное использование одного и того же кода.
Класс в ООП – является как бы конструктором, из которого вы можете далее создавать множество экземпляров:
>>> a = ThisIsClass() >>> b = ThisIsClass() >>> c = ThisIsClass() >>> id(a) 140249307072992 >>> id(b) 140249307114528 >>> id(c) 140249307114600 >>> a.attribute 'This is attribute value' >>> b.attribute 'This is attribute value' >>> c.attribute 'This is attribute value'
Каждый из этих экземпляров имеет свою область видимости и собственное пространство имён.
По умолчанию – все атрибуты класса доступны “снаружи”, т.е. – из других областей видимости.
Что бы создать т.н. “приватный” атрибут – используйте символ одинарного или двойного подчёркивания:
>>> class ThisIsClass: ... __attribute = 'This is attribute value' ... >>> cl = ThisIsClass >>> cl.attribute ... AttributeError: ThisIsClass instance has no attribute 'attribute' >>> cl.__attribute ... AttributeError: ThisIsClass instance has no attribute '__attribute'
Мы рассмотрим “приватные” аргументы и методы далее, в Инкапсуляции.
Методы классов
Каждый класс может содержать несколько методов класса.
По сути – метод, это обычная функция, например:
>>> class ThisIsClass: ... def method(self): ... print 'This is class method' ... >>> cl = ThisIsClass() >>> cl.method() This is class method
Методы могут принимать любые другие аргументы:
>>> class ThisIsClass: ... def method(self, a, b): ... print (a, b) ... >>> cl = ThisIsClass() >>> cl.method(1, 2) (1, 2)
self
Тут мы встречаемся со специальным аргументом метода – self
.
Его имя – необязательно, но общепринято использовать именно такое имя. Оно так же требуется для получения доступа к атрибутам класса.
self
указывает на экземпляр класса, из которого вызывается метод или атрибут:
>>> class ThisIsClass(): ... def method(self): ... print id(self) ... >>> cl = ThisIsClass() >>> cl.method() 140249307072920 >>> id(cl) 140249307072920
Кроме того, self
потребуется для доступа к атрибутам класса:
>>> class ThisIsClass: ... a = 1 ... b = 2 ... def method(self): ... print self.a, self.b ... >>> cl = ThisIsClass() >>> cl.method() 1 2
>>> class ThisIsClass: ... a = 1 ... b = 2 ... def method(self): ... print a, b ... >>> cl = ThisIsClass() >>> cl.method() __main__.ThisIsClass __main__.ThisIsClass
Свойства классов
Наследование (inheriting) классов
До сих пор вы создавали класс так:
class ThisIsClass:
Однако, классы в Python поддерживают механизм наследования, когда дочерний класс наследует атрибуты и методы из родительского класса.
Для того, что бы указать, что у класса есть родительских класс – он указывается в скобках:
>>> class ParentClass: ... parentclassattribute = 'parentclassattribute' ... >>> class InheritingClass(ParentClass): ... def method(self): ... print self.parentclassattribute ... >>> cl = InheritingClass() >>> cl.method() parentclassattribute
В этом примере InheritingClass
наследует значение атрибута parentclassattribute
из родительского – ParentClass
класса.
Тоже касается и методов классов:
>>> class ParentClass: ... def paerntmethod(self): ... print ('This is parent') ... >>> class InheritingClass(ParentClass): ... pass ... >>> cl = InheritingClass() >>> cl.paerntmethod() This is parent
Дочерний класс InheritingClass
использует метод paerntmethod
родительского класса.
Кроме того, дочерний класс может переопределить метод (или атрибут) родительского класса:
>>> class ParentClass: ... classattribute = 'classattribute' ... def method(self): ... print 'This is parent method' >>> class InheritingClass(ParentClass): ... classattribute = 'Inherited class attribute' ... def method(self): ... print ('This is child') ... >>> cl = InheritingClass() >>> cl.classattribute 'Inherited class attribute' >>> cl.method() This is child
Дочерний класс InheritingClass
перезаписывает данные родительского атрибута classattribute
своим значением, и переопределяет поведение родительского метода method()
.
Допустимо также множественное наследование:
>>> class FirstParentClass: ... f_attr = 1 ... >>> class SecondParentClass: ... s_attr = 2 ... >>> class InheritingClass(FirstParentClass, SecondParentClass): ... def method(self): ... print self.f_attr, self.s_attr ... >>> cl = InheritingClass() >>> cl.method() 1 2
Инкапсуляция
Мы уже кратко касались этой темы выше. Инкапсуляция – это скрытие каких-то данных – атрибутов или методов – от внешней области видимости.
Например – публичный атрибут:
>>> class ThisIsClass: ... public__attr = 'public_attr' ... >>> cl = ThisIsClass() >>> cl.public__attr 'public_attr'
И – скрытый, “инкапсулированный”, атрибут:
>>> class ThisIsClass: ... __privat_attr = 'privat_attr' ... >>> cl = ThisIsClass >>> cl.__privat_attr ... AttributeError: class ThisIsClass has no attribute '__privat_attr'
Пример с обычным публичным методом класса:
>>> class ThisIsClass: ... def pub_method(self): ... print 'This is public method' ... >>> cl = ThisIsClass >>> cl = ThisIsClass() >>> cl.pub_method() This is public method
И инкапсулированным:
>>> class ThisIsClass: ... def __privat_method(self): ... print 'This is privat method' ... >>> cl = ThisIsClass() >>> cl.__privat_method() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: ThisIsClass instance has no attribute '__privat_method'
При этом – эти атрибуты и методы будут доступны для использования внутри самого класса:
>>> class ThisIsClass: ... def __privat_method(self): ... return 'This is privat method' ... def pub_method(self): ... print self.__privat_method() ... >>> cl = ThisIsClass() >>> cl.pub_method() This is privat method
Однако, следует учитывать, что полностью приватным метод или атрибут в Python сделать нельзя:
>>> class ThisIsClass: ... def __privat_method(self): ... print 'This is privat method' ... >>> cl = ThisIsClass() >>> cl._ThisIsClass__privat_method() This is privat method
Полиморфизм
Полиморфизм – возможность использовать одно и то же имя метода к разным объектам разных классов.
Например:
>>> class FirstClass: ... def method(self, data): ... return data + 10 ... >>> class SecondClass: ... def method(self, data): ... return data + 'string' ... >>> cl1 = FirstClass() >>> cl2 = SecondClass() >>> cl1.method(10) 20 >>> cl2.method('string') 'stringstring'
Специальные методы классов
Специальных методов классов в Python очень много, поэтому – рассмотрим только два основных.
Наиболее часто встречающийся – __init__
.
Он является “конструктором класса“, и вызывается каждый раз при создании экземпляра класса.
Например:
>>> class ThisIsCLass: ... def __init__(self): ... print 'This is init method' ... >>> cl = ThisIsCLass() This is init method
С его помощью удобно создавать аргументы, которые в дальнейшем будут использоваться другими методами класса:
>>> class ThisIsCLass: ... def __init__(self, name): ... self.name = name ... >>> cl = ThisIsCLass('username') >>> cl.name 'username'
>>> class ThisIsCLass: ... def __init__(self, name): ... self.name = name ... def method(self): ... print self.name ... >>> cl = ThisIsCLass('username') >>> cl.method() username
Метод __doc__
– выводит строку документации (docstring) класса:
>>> class ThisIsCLass: ... """This is doctring""" ... pass ... >>> cl = ThisIsCLass() >>> cl.__doc__ 'This is doctring'