ЛАБОРАТОРНАЯ РАБОТА №12

Тема: Наследование классов

Цель: Рассмотрение способов создания новых классов путем их наследования

ОГЛАВЛЕНИЕ

1 Консервация классов
2 Наследование классов
2.1 Наследование класса Virt_zoo
2.2 Множественное наследование
2.3 Наследование неизменяемых классов
2.4 Создание пользоваетельских последовательностей
Индивидуальные задания

 Оглавление

1 Консервация классов

Над объектами – экземплярами классов, как и над другими объектами языка 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 осуществлены успешно.

 Оглавление

2 Наследование классов

2.1 Наследование класса Virt_zoo

Для проверки наследования классов создается класс 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)
А ты кто?
Я – Серый волк.
Серый волк, Серый волк. А давай дружить!

 Оглавление

2.2 Множественное наследование

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
Золотая рыбка

 Оглавление

2.3 Наследование встроенных классов

Сначала рассмотрим наследование встроенных неизменяемых классов. Для этого создадим класс, который наследует класс 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

 Оглавление

2.4 Создание пользоваетельских последовательностей

В качестве примера создания пользовательской последовательности рассмотрим класс 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 обладает следующими функциональными возможностями, реализуемых его методами: