Python Tutorial 4
9. Классы
Классы в питоне - это смесь 2-х механизмов , взятых от C++ и Modula-3.
Основные фичи - множественная наследование , перегрузка базовых методов .
Все члены класса , включая данные , имеют тип public, и все функции virtual.
Нет специальных конструкторов или деструкторов .
Классы сами по себе обьекты , в питоне все типы данных - обьекты.
Встроенные операторы (напр,арифметические) имеют специфический синтаксис
и могут быть переопределены для обьектов класса .
Обьекты индивидуальны . У одного обьекта может быть несколько имен (алиасов).
Алиас - в некотором смысле указатель на обьект .
namespace - пространство имен обьектов .
Примеры namespaces : встроенные имена (такие как функция abs());
глобальные имена модуля; локальные переменные функций . Между этими namespaces нет абсолютно
никакой связи - например 2 разных модуля могут иметь 2 разных функции с одни и тем же названием``maximize''.
Термин attribute справедлив для любого имени , следующего за точкой - например в выражении z.real , real есть атрибут обьекта z .
Ссылка на имя в модуле - ссылка на атрибут : в выражении modname.funcname , modname есть модуль и
funcname - его атрибут .
Атрибуты могут быть на чтение или запись . Во втором случае возможна операция присваивания .
Для атрибута модуля можно написать : "modname.the_answer = 42".
Атрибут модуля может быть удален с помощью операции del .
Например : "del modname.the_answer" .
Разные Namespaces создаются в разные моменты времени . Например , namespace для built-in имен
создается в момент запуска интерпретатора Python .
namespace для модуля создается в момент , когда читается его определение .
Исполняемый код также имеет собственный глобальный namespace.
Локальный namespace для функции создается при ее вызове и удаляется при выходе .
Хотя удаляется - это сказано круто , правильнее сказать - как-бы забывается
и позднее будет ликвидирован сборщиком мусора .
scope - область видимости , в которой доступен namespace.
В любой момент времени доступны сразу несколько scopes :
innermost scope - включает локальные имена и функции ;
middle scope - глобальные имена текущего модуля;
outermost scope - built-in идентификаторы .
Если имя глобально , оно лежит в middle scope .
Все переменные за пределами innermost scope только на чтение .
Функция ищет глобальные переменные внутри своего модуля .
Операция присваивания в питоне выполняется всегда в innermost scope.
Присваивание - это не копирование данных , это просто привязка имен к обьектам .
То же справедливо и для удаления - операция "del x" удаляет привязку x .
9.3.1 Инициализация классов
Простейшая форма определения класса :
class ClassName:
<statement-1>
.
.
.
<statement-N>
Класс можно определить в любом месте - хоть внутри функции .
Внутри класса идут определения функций .
Вместе с классом создается новый namespace .
Если внутри класса все определения корректны , создается обьект класса .
9.3.2 Обьект класса
Обьект класса может быть ссылкой или инстансом .
Ссылка стандартна для питона : obj.name . Например :
class MyClass:
"A simple example class"
i = 12345
def f(self):
return 'hello world'
После чего можно обратиться по ссылке MyClass.i и MyClass.f .
К атрибутам класса можно применить операцию присваивания .
При создании инстанса используется функциональная нотация .
При этом обьект класса есть параметризованная функция , которая возвращает обьект класса :
При этом создается пустой обьект .
Класс может иметь специальный метод
__init__():
def __init__(self):
self.data = []
При создании инстанса метод __init__() вызывается автоматом .
__init__() может иметь аргументы :
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
9.3.3 Инстанс - обьекты .
В классах возможны 2 типа атрибутов - первый : data attributes.
Это примерно то же самое , что и ``data members'' в C++.
Например , если x - инстанс MyClass ,
следующий код распечатает 16 :
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print x.counter
del x.counter
Второй тип атрибутов,который понимают инстансы,называется methods.
Любая функция класса может быть атрибутом-методом для инстанса .
Например, x.f верная ссылка на метод, поскольку
MyClass.f - функция, но x.i неверно, поскольку
MyClass.i не существует. Но x.f - это не то же самое , что и
MyClass.f -- это метод, а не
функция.
9.3.4 Методы
Вызов метода :
В нашем примере будет возвращена строка 'hello world' .
Аналогично :
xf = x.f
while True:
print xf()
Внутри класса можно сделать ссылку на функцию , которая определена вне класса :
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)
class C:
f = f1
def g(self):
return 'hello world'
h = g
Методы могут вызывать другие методы :
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
Методы могут ссылаться на глобальные имена .
9.5 Наследование
Синтаксис для определения производного класса :
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
Должно быть определено имя базового класса BaseClassName.
Можно также ссылаться на базовый класс из другого модуля .
class DerivedClassName(modname.BaseClassName):
Поведение производного класса аналогично базовому : если атрибут не найден в классе ,
он ищется в базовом классе .
Производный класс может перегружать методы базового класса .
Напрямую базовый класс можно вызвать так :"BaseClassName.methodname(self, arguments)".
9.5.1 Множественное наследование
Определение класса с множественным наследованием :
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
При этом порядок наследования следующий : если атрибут не найден в DerivedClassName, он ищется в Base1, затем (рекурсивно) в базовом классе Base1,
затем в Base2, и так далее .
Для питона известна проблема , когда класс унаследован от 2-х классов , имеющих общий базовый класс .
9.6 Private переменные
Поддержка этого типа переменных специфична для питона .
Такая переменная должна быть определена с префиксом _classname__spam , где classname имя класса. Тип private может быть как у данных , так и у методов .
Для создания типа , аналогичного типу``struct'' из C , можно использовать пустой класс :
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
9.8 Exceptions
Имеются 2 новых формы исключений :
raise Class, instance
raise instance
В первом случае , instance должен быть инстансом Class или класса , производного от него . Второй вариант есть аналог для :
raise instance.__class__, instance
В следующем примере будет распечатано B, C, D :
class B:
pass
class C(B):
pass
class D(C):
pass
for c in [B, C, D]:
try:
raise c()
except D:
print "D"
except C:
print "C"
except B:
print "B"
Если поставить первым "except B" , будет напечатано B, B, B .
9.9 Итераторы
В любом контейнере можно использовать цикл с использованием for :
for element in [1, 2, 3]:
print element
for element in (1, 2, 3):
print element
for key in {'one':1, 'two':2}:
print key
for char in "123":
print char
for line in open("myfile.txt"):
print line
У контейнера имеется 2 метода - iter() и next() .
Их использование показано в примере :
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
it.next()
StopIteration
Использование итераторов в классе :
class Reverse:
"Iterator for looping over a sequence backwards"
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> for char in Reverse('spam'):
... print char
...
m
a
p
s
9.10 Генераторы
Генераторы - средство для создания итераторов .
Используется ключевое слово yield , которое заменяет return :
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
... print char
...
f
l
o
g
Генератор может быть выражением :
>>> sum(i*i for i in range(10)) # sum of squares
285
>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
260
>>> from math import pi, sin
>>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))
>>> unique_words = set(word for line in page for word in line.split())
>>> valedictorian = max((student.gpa, student.name) for student in graduates)
>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']
|