Лутц М. - Изучаем Python (1077325), страница 139
Текст из файла (страница 139)
Множественное наследование В строке заголовка инструкции с]ааа в круглых скобках может быть перечислено более одного суперкласса. В этом случае используется то, что называется множественным наследованием, — класс и его экземпляры наследуют имена всех перечисленных суперклассов. При поиске атрибутов интерпретатор РуЬ]топ выполняет обход суперклассов, указанных в строке заголовка класса, слева направо, пока не будет найдено первое совпадение.
С технической точки зрения поиск сначала продолжается по направлению снизу вверх всеми возможными путями, вплоть до вершины дерева наследования, а затем слева направо, потому что любой суперкласс может иметь собственные суперклассы. Вообще множественное наследование хорошо использовать для моделирования объектов, принадлежащих более чем одной группе. Например, бБО Глава 25. Шаблоны проектирования с классами человек может быть инженером, писателем, музыкантом и т.
д., и наследовать свойства всех этих групп. Пожалуй, самый распространенный случай, где используется множественное наследование, — это »смешивание» методов общего назначения из нескольких суперклассов. Обычно такие суперклассы называются классами-смесями — они предоставляют методы, которые добавляются в прикладные классы наследованием. Например, способ вывода экземпляра класса, используемый в РуСЬоп по умолчанию, не отличается информативностью: »> с1авв Зраа; Еет 1п11 (ве1Г): № Нег кегодв герг ве)Г.Еатат Гооо" »> х зрвв() »> рг1пт Х № По умов»ение класс, вдрес вате .Брав тпвтепсе ет Ох00884818> Как было отмечено в предыдущем разделе, где рассматривалась перегрузка оператора, существует возможность с помощью метода герт реализовать свою собственную операцию вывода.
Но вместо того чтобы воспроизводить метод герг в каждом классе, который предполагается выводить на экран, почему бы не написать его всего один раз в классе инструментов общего назначения и не наследовать его во всех ваших классах2 Для этого и используются классы-смеси. В следующем файле лту1оо(в ру определяется класс-смесь с именем стзте г, переопределяющий метод герг в каждом классе, который наследует его. Он просто сканирует словарь атрибутов экземпляра (экспортируется в виде атрибута 01ст ) и собирает строку из имен и значений всех атрибутов экземпляра.
Так как классы — это объекты, логика форматирования класса с18тег может использоваться и для экземпляров любых подклассов — зто универсальный инструмент.' Класс ьтэтег использует два специальных приема, чтобы получить из экземпляра нмя класса и адрес. Каждый экземпляр имеет встроенный атрибут с1азэ, указывающий на класс, из которого он был создан, и каждый класс имеет атрибут паве, указывающий на имя в заголовке, поэтому выражение зе18. с1888 . паве возвращает имя класса экземпляра. Получить адрес экземпляра в памяти можно с помощью встроенной функции 10, способной возвращать адрес любого объекта (по определению, уникальный идентификатор объекта): Альтернативный способ реализации похожего поведения можно найти в примере модуля регвопгру в конце главы 24.
В нем также просматривают. ся словари пространств имен, но там не предполагается пропуск имен, начинающихся н заканчивающихся двумя символами подчеркивания. 651 Множественное наследование авиаввваввввавввкааавлвавваввлавзалаввеакааааваавалвавывваааааааавввпалваава в (1зсег иожно подмеюивать в любые классы, чтоби обеспечить форматированной В вывод экземпляров через наследование метода герт , реализованного здесь, В ве1( — эта экземпляр сапого нижнего класса.
авввавваавзааавввааэвввааааввааввавааввзваавввзвавазававвваааваааввввавааааа с1авв Изсег: бес герт (ве11): гесогп ("<1пвсапсе ог %з, аббгевв %в;ч,пвз>" % (зе!г. с1авв . паве , в имя класса сб(зе1Г), В Адрес ве1(,ассгпаюев()) ) Ф список имя=значение бет аССгпавев(ве1Г): гево11 = '' гог ассг сп ве1(, б1сс .кеув(): в Словарь имен экземпляРа сс аттг[:2] == ' гезо11 = гево11 ь "'чспаюе %в=<ьос11-1п>Кп" % ассг е1ве. гезо11 = гево11+' Кспаюе %в=вз1п" % (асс<, зе1(.
бссс [ассг)) гетогп гево1С Экземпляры, унаследовавшие этот класс, при выводе будут автомати- чески отображать свои атрибуты, давая о себе больше информации, чем просто свой адрес: »> Ггоа аувоо1в 1арогС 'ь1зтег »> с1авз Зраа((1втег); бес 1п1С (ве)г); зе)у.бата) * 'Гооб' »> х Зраа() »> х <1пвсапсе ог зраа, аббгевв 8821568; паве баса1=гооб > Ггов юутоо1в 1юрогС Ызтег В Получить класс с инструментами с1авв Зорег: бес 1п!с (ве1(); ве1(.баса1 = "враз" В Метод суперкласса 1лст с1авв ЗоЬ(Зорег, [свтег): бес сп1с (зе1(): Зорег.
1псс (ве1Г) зе1(.баса2 = "е99в' ве1(.басаз = 42 в Наследовать герт иэ клвссв-снеси В (ээтег имеет доступ к зе) Г В Дополнительные втрибути экземпляра Класс [1ЗСег будет полезен для наследования любыми вашими классами, у которых уже есть суперкласс. Это тот случай, где удобно использовать множественное наследование: добавляя класс [1ЗСег в список суперклассов в строке заголовка класса (вмешивая его), вы получаете в свое распоряжение его метод герт, что не мешает наследовать другой существующий суперкласс.
Это демонстрирует файл 1ез(пс1х(л.ру: Глава 25, Шаблоны проектирования с классами тт паве == " вв!п Х = 3ОЬ() ргтят Х Ф ГЕРГ ПЗ КпаССа-СИЕОИ Здесь класс ЯОЬ наследует имена из двух классов, Ясрег и Ь!31ег — этот объект состоит из своих собственных имен и из имен обоих суперклассов. Если создать и вывести экземпляр класса ЗОЬ, автоматически будет получено адаптированное представление, подмешанное из класса 113!в г: С:'т1рзе> рутпеп 1евтв1х1п.
ру <1пв1апсе ОГ 3ОЬ, аООгввв 7833392; паве Оа1а3442 Паев ОВ1З2=В99В паве Овтз1=прав > Метод класса Ь!31ег будет работать в любом классе, унаследовавшем его, потому что аргумент ве) Г ссылается на экземпляр подкласса, который наследует 1131ег, каким бы этот подкласс ни был. Если позднее вы решите усовершенствовать класс Ь!все г, чтобы метод герт выводил также все атрибуты класса, которые были унаследованы экземпляром, вы без опаски сможете сделать это — так как это наследуемый метод, изменение в реализации Ь!31ег, герг автоматически начнет действовать и во всех подклассах, которые импортируют его.' В некотором смысле, классы-смеси — это классы, эквивалентные модулям, потому что они упаковывают методы, которые будут полезны самым разным клиентам. Ниже демонстрируется работа класса Гувтег в режиме единственного наследования с экземплярами различных классов: »> Гсов ву1со1в 1врогт 11в1ег »> с)ввв х(11втвг); рввв »> 1 = х() Если вам любопытно, как зто произойдет, вернитесь назад к разделу «Словари пространств имен«в главе 24.
Там мы видели, что у кюклого класса имеется свой атрибут Ьввев, который является кортежем объектов суперклассов. Универсальный класс просмотра иерархии может выполнять обход дерева наследования от экземпляра класса с)ввв к его классу и затем с помощью атрибута Ьвввв ко всем его суперклассам рекурсивно, как это делается в модуле с)аввтгвефу, показанном ранее.
В версии Рувйоп 2.2 и более поздних реализовать зто можно еще проще, поскольку теперь встроенная функция О !г автоматически включает имена унаследованных атрибутов. Если не требуется отображать структуру дерева наследования, то можно просто сканировать список, получаемый от функции О !г, вместо просмотра списка ключей словаря и использовать функцию рета11г для получения значения атрибута по его имени, заданному в виде строки, вместо обхода словаря.
Мы еще вернемся к этой идее з одном из заключительных примеров к этой части. Классы — это объекты: фабрики универсальных объектов 653 те=2; тс=з »> т а = т; »> с1пятапсе от паве паве паве > х, ассгеяя 7797696. Ь=2 а=т с=З ООП тесно связано с повторным использованием программного кода, и классы-смеси в этом отношении представляют собой мощный инструмент.
Как почти все в программировании, множественное наследование может быть благом при грамотном применении; но при неаккуратном и чрезмерном употреблении эта возможность может осложнить вам жизнь. Мы вернемся к этому вопросу как к одной из типичных проблем в конце следующей главы. В этой главе мы также познакомимся с возможностью (в классах нового стиля) изменять порядок поиска для одного специального случая множественного наследования.