Над объектами – экземплярами классов, как и над другими объектами языка Python, могут быть осуществлены операции сериализации (представление объектов в строковом виде) и консервации (запись строкового представления объекта на диск), а также обратные им операции десериализации и деконсервации (см. подраздел 2.2.1 лаб. раб. №10). Для выполнения этих операциий создается класс My_pickle, который имеет два метода:
>>>
class
My_pickle:
import
pickle
def
__init__
(self,file):
self.file=file
def
put
(self,obj):
f=
open
(self.file,
'wb'
)
My_pickle.pickle.dump(obj,f)
f.close()
def
get
(self):
with
open
(self.file,
'rb'
)
as
f:
return
My_pickle.pickle.load(f)
Модуль pickle описан как атрибут класса.
Поэтому при доступе к его методам необходимо указывать имя класса:
My_pickle.pickle.dump(obj,f).
Для проверки класса My_pickle и его методов
создадим объект p1 класса My_pickle, для
которого в конструкторе класса укажем имя файла "p1.dat" текущего
каталога:
>>>
p1=My_pickle(
"p1.dat"
),
и объект v1 класса Virt_zoo
(см. подраздел 2.3 лаб. раб. №11):
>>>
v1=Virt_zoo(
'Колобок'
,
'превосходно.'
)
Появилась новая зверюшка.
С помощью метода put() объекта
p1 осуществим консервацию объекта v1 на диске
в файле "p1.dat":
>>>
p1.put(v1)
С помощью метода get() объекта
p1 осуществим деконсервацию данных объекта, сохраненных на диске, в
объект v_new:
>>>
v_new=p1.get()
И в заключение для объекта v_new выполним
метод talk():
>>>
v_new.talk()
Привет! Меня зовут Колобок. Чуствую себя превосходно.
Т.е. операции консервации и деконсервации объекта
v1 осуществлены успешно.
Для проверки наследования классов создается класс Smart_zoo() на базе выше рассмотренного класса Virt_zoo() (см. подраздел 2.3 лаб. раб. №11). При этом осуществляется:
>>>
class
Smart_zoo
(Virt_zoo):
def
__init__
(self, name, mood, where):
print
(where, end=
""
)
super
().__init__(name, mood)
def
talk
(self):
print
(
"Я – "
+self.name+
"."
)
def
tell
(self,obj):
print
(
"А ты кто?"
)
obj.talk()
print
(obj.name,
","
obj.name,
"."
, sep=
""
, end=
""
)
if
self.name==
"Серый волк"
:
print
(
" А я тебя съем!"
)
else
:
print
(
" А давай дружить!"
)
Отметим, что при вызове конструктора класса
Smart_zoo – метода __init__ сначала
происходит вывод значения нового атрибута where, указывающего
местонахождение зверюшки. Затем с помощью встроенной функции super()
осуществляется вызов метода __init__ родительского класса
Virt_zoo, который выполняет все действия, предусмотренные конструктором
этого класса (см. подраздел 2.3 лаб. раб. №11).
Функция super([type[,object_or_type]])
возвращает объект, который делегирует вызов метода родительскому классу, указанному параметром
type. Это полезно для доступа к наследуемым методам, которые
переопределяются в классе. Если класс является единственным наследником, то параметры функции
super() можно опустить, т.е. для класса
Smart_zoo выражение
super
(Smart_zoo,self).__init__(name, mood)
эквивалентно
super
().__init__(name, mood).
Выполним инстанцирование двух объектов нового класса:
>>>
s1=Smart_zoo(
'Колобок'
,
'превосходно.'
,
'Волшебный лес. '
)
Волшебный лес. Появилась зверюшка.
>>>
s2=Smart_zoo(
'Серый волк'
,
'очень голодным.'
,
'Там же. '
)
Там же. Появилась зверюшка.
С помощью специального атрибута __class__
можно указать для объекта любой класс в иерархии его наследования:
>>>
s1.__class__=Virt_zoo
С помощью методов talk() и
tell() класса Smart_zoo реализуем два
варианта диалога между двумя созданными зверюшками: Колобком (объект s1) и Серым волком (объект
s2). В первом варианте разговор начинает Серый волк:
>>>
s2.talk()
Я – Серый волк.
>>>
s2.tell(s1)
А ты кто?
Привет! Меня зовут Колобок. Чуствую себя превосходно.
Колобок, Колобок. А я тебя съем!
Для объекта s1 был указан класс
Virt_zoo и s1 выполнил метод
talk() в "стиле" этого класса, т.е. вывел на экран строку
"Привет! Меня зовут Колобок. Чуствую себя превосходно.", вместо "Я – Колобок.".
Во втором варианте разговор начинает Колобок:
>>>
s1.talk()
Я – Колобок.
>>>
s2.tell(s1)
А ты кто?
Я – Серый волк.
Серый волк, Серый волк. А давай дружить!
Python поддерживает также множественное наследование. Рассмотрим создание класса New_zoo(), который имеет одновременно два базовых класса: Smart_zoo и My_pickle. Этот класс:
>>>
class
New_zoo
(Smart_zoo, My_pickle):
def
__init__
(self, name, mood, where, file):
print
(where,
'Появилалсь зверюшка.'
)
self.name=name
self.file=file
def
tell
(self,obj):
print
(
'А ты кто? '
)
obj.talk()
print
(obj.name+
", я сохраню твои данные."
)
self.put(obj)
Для проверки создадим один объект класса Smart_zoo:
>>>
s1=Smart_zoo(
'Золотая рыбка'
,
'чудесно'
,
'Море. '
)
Море. Появилась зверюшка.
и один объект объект класса New_zoo:
>>>
new1=New_zoo('
'Колобок'
,
'хорошо.'
,
'Берег моря.'
,
'obj.dat'
)
Берег моря. Появилась зверюшка.
>>>
new1.talk()
Я – Колобок.
>>>
new1.tell(s1)
А ты кто?
Я – Золотая рыбка.
Золотая рыбка, я сохраню твои данные.
>>>
s2=new1.get()
>>>
s2.name
Золотая рыбка
Сначала рассмотрим наследование встроенных неизменяемых классов. Для этого создадим
класс, который наследует класс float и осуществляет вычисление
обратного значения заданной величины:
>>>
class
Rev
(
float
):
'''Получение обратной величины'''
def
__init__
(self,arg):
arg=arg.__rtruediv__(1)
>>>
Rev(7)
7.0
Результат показывает, что было возвращено исходное значение заданного аргумента (7.0) без
вычисления его обратного значения. Это произошло потому, что при инстанцировании класса
Rev() был получен экземпляр объекта неизменяемого типа и поэтому его
значение не могло быть изменено при инициализации.
Решением является перенос процесса вычисления обратной величины туда,
где новый объект еще не создан. А это может быть только блок (тело) специального метода
__new__(), который вызывается самым первым при инстанцировании класса:
>>>
class
Rev
(
float
):
'''Получение обратной величины'''
def
__new__
(cls,arg):
arg=arg.__rtruediv__(1)
return
float
.__new__(cls,arg)
>>>
Rev(7)
0.14285714285714285
Рассмотрим еще один пример наследования неизменяемого класса. Создадим
класс W, который наследует встроенный неизменяемый класс
str и переопределяет в нем операции сравнения с помощью специальных
методов. Вместо того, чтобы сравнивать слова между собой по величине кода их символов, как это
делается в стандартных строках, объекты класса W, которыми являются
слова, будут сравниваться по их длине, т.е. по числу символов:
>>>
class
W
(
str
):
'''Сравнение слов по их длине'''
def
__new__
(cls,word):
if
' '
in
word:
word=word[:word.index(
' '
)]
return
str
__new__(cls,word)
def
__gt__
(self,other):
return
len
(self)>
len
(other)
def
__ge__
(self,other):
return
len
(self)>=
len
(other)
def
__le__
(self,other):
return
len
(self)<=
len
(other)
def
__lt__
(self,other):
return
len
(self)<
len
(other)
Создадим путем инстанцирования объекты a,
b класса str и объекты
aw, bw класса W:
>>>
a=
'cat'
>>>
aw=W(
'cat'
)
>>>
b=
'z'
>>>
bw=W(
'z'
)
и проверим операции сравнения объекта a с
объектом b и объекта aw с объектом
bw:
>>>
a>b
False
>>>
aw>bw
True
>>>
a<b
True
>>>
aw<bw
False
>>>
a==b
False
>>>
aw==bw
False
Результаты показывают, что сравнение строк
a и b осущестляется по величине кода их первых
символов, а сравнение строк aw и bw –
по их длине. Отметим, что сравнение строк aw и
bw на равенство (операция "==") осуществлялось с использованием метода
__eq__ (self,other) базового класса str,
поскольку для класса W этот метод не был определен.
Рассмотрим пример наследования класса int с
перегрузкой операции сложения. Для этого создадим класс Mod3, который
:
>>>
class
Rev
(
float
):
'''Получение обратной величины'''
def
__init__
(self,arg):
arg=arg.__rtruediv__(1)
>>>
Rev(7)
7.0
В качестве примера создания пользовательской последовательности рассмотрим класс
My_list, функционально близкий к списку, но имеющий ряд методов, которых
нет в классе list, и в то же время не поддерживающий некоторые методы
списка:
>>>
class
My_list
:
''' Пользовательский класс, близкий к списку '''
def
__init__
(self,values=
None
) :
if
values
is None
:
self.values=[]
else
:
self.values=values
def
__getitem__
(self,key):
''' Получить значение по ключу '''
return
self.values[key]
def
__setitem__
(self,key,value):
''' Установить значение по ключу '''
self.values[key]=value
def
first
(self):
''' Получить значение первого элемента '''
return
self.values[0]
def
last
(self):
''' Получить значение последнего элемента '''
return
self.values[-1]
def
__iter__
(self):
''' Сделать последовательность итерабельной '''
return
iter
(self.values)
def
__str__
(self):
''' Возвратить строковое представление объекта '''
return
'My_list: '
str
(self.values)
def
__len__
(self):
''' Возвратить длину последовательности '''
return
len
(self.values)
def
__delitem__
(self,key):
''' Удалить элемент '''
del
self.values[key]
def
push
(self,value):
''' Добавить элемент в конец последовательности'''
self.values[
len
(self):
len
(self)]=value
Класс My_list обладает следующими функциональными возможностями, реализуемых его методами:
Разработать программу на языке Python, в которой необходимо:
Номер п/п | Преобразование | Методы |
---|---|---|
1 | 1 | 1,2,3,4,6,7 |
2 | 2 | 1,2,3,5,7,8 |
3 | 3 | 1,2,3,6,8,9 |
4 | 4 | 1,2,3,8,9,10 |
5 | 5 | 1,2,4,5,7,10 |
6 | 6 | 1,2,3,4,6,7 |
7 | 7 | 1,2,3,5,7,8 |
8 | 1 | 1,2,3,6,8,9 |
9 | 2 | 1,2,3,8,9,10 |
10 | 3 | 1,2,4,5,7,10 |
11 | 4 | 1,2,3,4,6,7 |
12 | 5 | 1,2,3,5,7,8 |
13 | 6 | 1,2,3,6,8,9 |
14 | 7 | 1,2,3,8,9,10 |
15 | 1 | 1,2,4,5,7,10 |
16 | 2 | 1,2,3,4,6,7 |
17 | 3 | 1,2,3,5,7,8 |
18 | 4 | 1,2,3,6,8,9 |
19 | 5 | 1,2,3,8,9,10 |
20 | 6 | 1,2,4,5,7,10 |