диспетчеризация
Множественная диспетчеризация : Язык программирования Python : Клуб знатоков DataWarehouse, OLAP, XML : Intersoft LabО КОМПАНИИПубликацииВакансииКонтактыКЛИЕНТЫБанкиПредприятия газовой отраслиСтроительные организацииТорговые предприятияПроизводственные предприятияПредприятия сферы услугГосударственные структурыПАРТНЕРЫНаши партнерыВУЗы партнерыПартнерская программаУсловия для ВузовРешения ПартнеровПроекты ПартнеровПРАЙС-ЛИСТЫПлатформа Хранилищ данныхContour Business IntelligenceУСЛУГИУслуги диспетчеризация поддержкаПорядок оказания услугКЛУБЖурнал клуба знатоков DWH, OLAP, XMLНовости технологийБиблиотека ссылок по DWH, OLAP, XMLГлоссарий технологических терминовФОРУМENGLISH.title-header-a {margin-right: 30px;}Входдля Клиентовдля ПартнеровсегодняКЛУБ ЗНАТОКОВ DATAWAREHOUSE, OLAP, XML Множественная диспетчеризация
Дата: 20.03.2003
Дэвид Мертц (David Mertz)Перевод: Intersoft Lab
Обобщение полиморфизма с помощью мультиметодовВо многом универсальность объектно-ориентрованного программирования (ООП) возможна благодаря полиморфизму: в надлежащем контексте объекты разных видов могут вести себя различным образом. Однако большая часть ООП - это единичная диспетчеризация, то есть единственный выделенный объект определяет, какую ветвь кода избрать. Концептуально более общий подход - позволить всем аргументам, передаваемым в функцию/метод, определять ее специализацию. В этой статье рассматривается реализация множественной диспетчеризации в Python диспетчеризация показано, как это улучшает программы. Что такое полиморфизм?Большинство программистов, использующих полиморфизм - на Python или других языках объектно-ориентированного программирования -, находят ему весьма практическое диспетчеризация конкретное применение. Возможно, наиболее общий случай использования полиморфизма - это создание семейства объектов, которые придерживаются общего протокола. В Python это обычно просто вопрос нерегламентированного полиморфизма; в других языках чаще объявляются формальные интерфейсы, и/или эти семейства обладают общим предком.Например, существует множество функций, которые работают с объектами, "подобными файлам", где это подобие файлам определяется просто посредством поддержания нескольких методов, как .read(), .readlines() и, возможно, .seek(). Функция, как read_app_data(), может принимать аргумент src - когда мы вызовем эту функцию, мы, возможно, решим передать ей локальный файл, объект urllib, объект cStringIO или некий объект, определенный пользователем, который разрешает этой функции вызывать src.read(). Каждый тип объекта равнозначен с точки зрения того, как он функционирует в read_app_data().Давайте вернемся немного назад, чтобы понять, что здесь действительно происходит. По существу, нас интересует, как выбрать надлежащую ветвь кода для выполнения в контексте; старомодный процедурный код может принимать эквивалентные решения, ООП просто придает элегантность. Например, фрагмент процедурного (псевдо) кода мог бы выглядеть следующим образом:Листинг 1. Процедурный выбор ветвей кода по типу объекта...bind 'src' in some manner...if <>:read_from_file(src)elif <>:read_from_url(src)elif <>:read_from_stringio(src)...etc...Организовав поддержку общих методов объектами различных типов, мы перемещаем решение о диспетчеризации в объекты из явного условного блока. Просматривая дерево наследования, данный объект src узнает, какие блоки кода ему нужно вызывать. Однако, по-прежнему происходит неявное переключение, но по типу объекта src.Объект src привилегирован по отношению к любым аргументам, передаваемым в его методы. Из-за синтаксиса ООП эта привилегированность кажется неизбежной, но на самом деле это не так. Во многих случаях процедурное переключение просто переносится в тела методов классов. Например, мы могли бы реализовать совместимые по протоколу классы Foo диспетчеризация Bar следующим образом:Листинг 2. Реализация метода .meth() с помощью Foo диспетчеризация Barclass Foo:def meth(self, arg):if <>:...FooFoo code block...elif <>:...FooBar code block...class Bar:def meth(self, arg):if <>:...BarFoo code block...elif <>:...BarBar code block...# Function to utilize Foo/Bar single-dispatch polymorphismdef x_with_y(x, y):if <> and <>:x.meth(y)else:raise TypeError,"x, y must be either Foo's or Bar's"Имеется пять различных ветвей/блоков кода, которые могут выполняться при вызове x_with_y(). Если типы x диспетчеризация y не подходят, возбуждается исключение (разумеется, вы могли бы сделать что-нибуль другое). Но, предполагая, что с типами все в порядке, ветвь кода выбирается сначала посредством полиморфной диспетчеризации, диспетчеризация затем посредством процедурного переключения. Кроме того, переключения внутри определений Foo.meth() диспетчеризация Bar.meth() в значительной степени эквивалентны. Полиморфизм - в разновидности с единичной диспетчеризацией - решает лишь половину задачи.Полная реализация полиморфизмаВ случае полиморфизма с единичной диспетчеризацией выделяется объект, который "владеет" методом. Синтаксически в Python его выделяют, располагая его имя перед точкой - все, что следует за точкой: имя метода диспетчеризация левая скобка - просто аргумент. Но семантически этот объект является особенным при использовании дерева наследования для выбора метода.А что если бы мы обрабатывали особым образом не один объект, диспетчеризация позволили бы каждому объекту, задействованному в блоке кода, участвовать в выборе ветви выполнения? Например, мы могли бы выразить наше пятистороннее переключение более симметрично:Листинг 3. Множественная диспетчеризация Foo диспетчеризация Barx_with_y = Dispatch([((object, object), <>)])x_with_y.add_rule((Foo,Foo), <>)x_with_y.add_rule((Foo,Bar), <>)x_with_y.add_rule((Bar,Foo), <>)x_with_y.add_rule((Bar,Bar), <>)#...call the function x_with_y() using some arguments...x_with_y(something, otherthing)Я думаю, что эта симметричность полиморфной диспетчеризации по множеству аргументов гораздо более элегантна, чем предшествующий стиль. Кроме того, этот стиль позволяет документировать одинаковую роль этих двух объектов, задействованных в определении подходящей ветви кода.Стандартный Python не разрешает конфигурировать этот тип множественной диспетчеризации; но, к счастью, вы можете сделать это, воспользовавшись написанным мною модулем multimethods. См. Ресурсы, чтобы скачать этот модуль отдельно или в составе утилит Gnosis. После того, как вы установили multimethods, все, что от вас требуется - включить в начало своего приложения следующую строку:from multimethods import Dispatch"Мультиметоды", как правило, это синоним множественной диспетчеризации; но термин мультиметод предполагает конкретную функциональную/объектную реализацию более абстрактной концепции множественной диспетчеризации.Экземпляр Dispatch - это вызываемый объект, его можно конфигурировать с любым желаемым количеством правил. К тому же, можно использовать метод Dispatch.remove_rule(), чтобы удалять правила; благодаря этому множественная диспетчеризация с использованием multimethods становится несколько более динамичной, чем статическая иерархия классов (но вы также можете совершить некие замысловатые действия с классами Python во время исполнения). Также заметьте, экземпляр Dispatch может принимать переменное число аргументов; сопоставление выполняется сначала по числу аргументов, затем по их типам. Если экземпляр Dispatch вызывается с любым шаблоном, который не определен в правиле, возбуждается TypeError. Инициализация x_with_y() с запасным шаблоном (object,object) необязательна, если вы просто хотите, чтобы в неопределенных ситуациях возбуждалось исключение.Каждый кортеж (pattern,function), перечисленный в инициализации Dispatch, просто передается далее в метод .add_rule(); это исключительно вопрос удобства программирования - устанавливать правила при инициализации или позже (можно комбинировать подходы, как в предшествующем примере). При вызове функции из диспетчера аргументы, используемые при вызове, передаются диспетчеру; вы должны обеспечить, чтобы функция, которую вы используете, могла принять то число аргументом, с которым она сопоставляется. Например, приведенные ниже вызовы эквиваленты:Листинг 4. Явный вызов диспетчеризация вызов функции при диспетчеризации # Define function, classes, objectsdef func(a,b): print "The X is", a, "the Y is", bclass X(object): passclass Y(object): passx, y = X(), Y()# Explicit call to func with argsfunc(x,y)# Dispatched call to func on argsfrom multimethods import Dispatchdispatch = Dispatch()dispatch.add_rule((X,Y), func)dispatch(x,y) # resolves to 'func(x,y)'Очевидно, что если вы знаете типы x диспетчеризация y во время проектирования, алгоритм задания диспетчера - просто накладные расходы. Но то же ограничение справедливо диспетчеризация для полиморфизма - он удобен, лишь когда вы не можете ограничить объект единственным типом для каждой ветви исполнения.Улучшение наследованияМножественная диспетчеризация не просто обобщает полиморфизм, она предоставляет более гибкую альтернативу наследованию во многих контекстах. Рассмотрим в качестве иллюстрации следующий пример. Предположим, что вы пишете программу построения чертежей или автоматизированного проектирования, которая работает с различными фигурами (shape); в частности, вы хотите, чтобы вы могли комбинировать две фигуры таким образом, чтобы результат зависел от обеих задействованных фигур. Кроме того, набор рассматриваемых фигур будет расширяться производными приложениями или подключаемыми библиотеками. Расширение набора классов фигур является неизящным подходом при модернизации, например:Листинг 5. Наследование для расширения возможностей# Base classesclass Circle(Shape):def combine_with_circle(self, circle): ...def combine_with_square(self, square): ...class Square(Shape):def combine_with_circle(self, circle): ...def combine_with_square(self, square): ...# Enhancing base with triangle shapeclass Triangle(Shape):def combine_with_circle(self, circle): ...def combine_with_square(self, square): ...def combine_with_triangle(self, triangle): ...class NewCircle(Circle):def combine_with_triangle(self, triangle): ...class NewSquare(Square):def combine_with_triangle(self, triangle): ...# Can optionally use original class names in new contextCircle, Square = NewCircle, NewSquare# Use the classes in applicationc, t, s = Circle(...), Triangle(...), Square(...)newshape1 = c.combine_with_triangle(t)newshape2 = s.combine_with_circle(c)# discover 'x' of unknown type, then combine with 't'if isinstance(x, Triangle): new3 = t.combine_with_triangle(x)elif isinstance(x, Square): new3 = t.combine_with_square(x)elif isinstance(x, Circle): new3 = t.combine_with_circle(x)В частности, каждый существующий класс фигуры должен добавлять возможности потомку, что приводит к комбинаторной сложности диспетчеризация трудностям при сопровождении.Напротив, метод множественной диспетчеризации более прост:Листинг 6. Мультиметоды для расширения возможностей# Base rules (stipulate combination is order independent)class Circle(Shape): passclass Square(Shape): passdef circle_with_square(circle, square): ...def circle_with_circle(circle, circle): ...def square_with_square(square, square): ...combine = Dispatch()combine.add_rule((Circle, Square), circle_with_square)combine.add_rule((Circle, Circle), circle_with_circle)combine.add_rule((Square, Square), square_with_square)combine.add_rule((Square, Circle),lambda s,c: circle_with_square(c,s))# Enhancing base with triangle shapeclass Triangle(Shape): passdef triangle_with_triangle(triangle, triangle): ...def triangle_with_circle(triangle, circle): ...def triangle_with_square(triangle, square): ...combine.add_rule((Triangle,Triangle), triangle_with_triangle)combine.add_rule((Triangle,Circle), triangle_with_circle)combine.add_rule((Triangle,Square), triangle_with_square)combine.add_rule((Circle,Triangle),lambda c,t: triangle_with_circle(t,c))combine.add_rule((Square,Triangle),lambda s,t: triangle_with_square(t,s))# Use the rules in applicationc, t, s = Circle(...), Triangle(...), Square(...)newshape1 = combine(c, t)[0]newshape2 = combine(s, c)[0]# discover 'x' of unknown type, then combine with 't'newshape3 = combine(t, x)[0]Определение новых правил (и поддержка функций/методов) в значительной степени эквивалентны. Но огромное преимущество стиля множественной диспетчеризации - это цельность, с помощью которой вы комбинировать фигуры неизвестных типов. Вместо того, чтобы возвращаться к явным (и длинным) условным блокам, определения правил автоматически решают эти вопросы. Что еще лучше, все комбинирование выполняется одним вызовом combine(), диспетчеризация не с помощью "зверинца" из разных комбинирующих методов.Передача диспетчеризацииНе испытывая необходимости больше думать о диспетчеризации, класс multimethods.Dispatch будет выбирать "наилучшее совпадение" для данного обращения к диспетчеру. Однако, иногда стоит заметить, что "лучшее" не значит "единственное". То есть, обращение к dispatch(foo,bar) может давать точное совпадение с правилом (Foo,Bar) - но оно также может задавать менее точное совпадение (не промах!) для (FooParent,BarParent). Точно так, как иногда вы хотите вызывать методы базовых классов в методе производного класса, вы также иногда желаете вызывать менее специфические правила в диспетчере.Модуль multimethods позволяет задавать вызовы менее специфических правил как грубо, так диспетчеризация с тонкой настройкой. На грубом уровне, обычно вы просто хотите автоматически вызывать менее специфичное правило в начале, либо в конце выполнения блока кода. Подобным образом вы практически всегда вызываете метод надкласса в начале, либо в конце тела метода потомка. Общий вариант начального/конечного вызова менее специфичных методов может быть задан просто как часть правила. Например:Листинг 7. Автоматическое воспроизведение диспетчеризацииclass General(object): passclass Between(General): passclass Specific(Between): passdispatch = Dispatch()dispatch.add_rule((General,), lambda _:"Gen", AT_END)dispatch.add_rule((Between,), lambda _:"Betw", AT_END)dispatch.add_rule((Specific,), lambda _:"Specif", AT_END)dispatch(General()) # Result: ['Gen']dispatch(Specific()) # Result: ['Specif', 'Betw', 'Gen']Разумеется, в некоторых ситуациях (как для правила (General)) менее специфичное правило отсутствует. Для обеспечения единообразия, однако, каждое обращение к диспетчеру возвращает список значений из всех функций, которым передается управление таким образом. Если в правиле не определены ни AT_END, ни AT_START, распространение вызовов не производится (и возвращается список из одного элемента). Этим объясняется индекс [0] в примере с фигурами, который, вероятно, кажется загадочным .Для тонкой настройки распространения вызовов применяется метод диспетчера .next_method(). Чтобы задать распространение вызовов вручную, нужно использовать для определения правил метод .add_dispatchable(), диспетчеризация не метод .add_rule(). Кроме того, диспетчеризованные функции сами должны принимать аргумент dispatch. При вызове диспетчера вы либо должны передать аргумент, задающий диспетчер, либо вы можете воспользоваться вспомогательным методом .with_dispatch(). Например:Листинг 8. Программирование с ручной передачейdef do_between(x, dispatch):print "do some initial stuff"val = dispatch.next_method() # return simple value of up-callprint "do some followup stuff"return "My return value"foo = Foo()import multimethodsmulti = multimethods.Dispatch()multi.add_dispatchable((Foo,), do_between)multi.with_dispatch(foo)# Or: multi(foo, multi)Вызов менее специфичных мультиметодов вручную может оказаться запутанным - примерно так же, как диспетчеризация обращение к методам базовых классов. Чтобы эти вопросы стали управляемыми, обращение к .next_method() всегда возвращает простой результат вызова верхнего уровня - если вы хотите собрать такие результаты в список, как тот, что создает аргумент AT_END, вам нужно добавлять диспетчеризация обрабатывать те величины, которые вы считаете уместными. Наиболее общий вариант использования, однако - выполнение последовательности связанных инициализаций; в этом случае возвращаемые величины обычно неважны.Замечания выполнении в многонитевой средеСтоит привести краткое замечание, пока читатель не столкнулся с проблемой. Из-за необходимости сохранения состояния для отслеживания, какие (последовательно менее специфичные) правила вызывались, диспетчер не является нитебезопасным. Если нужно использовать диспетчер в многонитевой среде, необходимо "клонировать" его для каждой нити. Это ненакладно с точки зрения ресурсов: памяти диспетчеризация процессора, так что клонирование диспетчеров не вызывает существенных неудобств. Например, предположим, что функция могла бы вызываться из разных нитей; вы можете написать:Листинг 9. Клонирование для безопасности нитиdef threadable_dispatch(dispatcher, other, arguments)dispatcher = dispatcher.clone()#...do setup activities...dispatcher(some, rule, pattern)#...do other stuff...Если внутри threadable_dispatch() не запускаются новые нити, все нормально.Вам потребуется некоторое время, чтобы освоиться с идей множественной диспетчеризации, даже - или особенно - если вы весьма опытны в объектно-ориентированном программировании. Но после того, как вы немного с ней поэкспериментируете, вероятно, вы обнаружите, что множественная диспетчеризация обобщает диспетчеризация усиливает преимущества, которыми ООП обладает прежде всего над процедурным программированием.РесурсыВы можете получить multimethods как отдельный модуль, либо как часть пакета Gnosis Utilities.Gnosis Utilities выходит как Питоновский пакет distutils.Другие языки реализовали множественную диспетчеризацию либо в самом языке, либо в библиотеках. Например, MultiJava - расширенный набор Java, который реализует множественную диспетчеризацию.CLOS диспетчеризация Dylan используют множественную диспетчеризацию в качестве базиса своей системы ООП. Возможно, "Обсуждение подхода Dylan" (a discussion of Dylan's mechanism) покажется вам интересным.В Perl есть модуль под названием Class::Multimethods, предназначенный для реализации множественной диспетчеризации (и, по-видимому, предполагается, что в Perl 6 эта концепция будет более глубоко встроена в язык). Дэмиан Конуэей рассматривает этот модуль (Damian Conway discusses his module).Ресурсы для разработчиков Linux в зоне Linux developerWorks.Об автореДэвид Мертц предчувствует, что программисты, страдающие синдромом раздвоения личности, захотят, чтобы все их функции были общими. Дэвид доступен по адресу: mertz@gnosis.cx, диспетчеризация жизнь его описана на http://gnosis.cx/publish/. Присылайте свои замечания диспетчеризация предложения касательно этой, прошлых или будущих статей.Оригинальный текст статьи можно посмотреть здесь:
Multiple dispatch
Просмотреть все статьи рубрики >>
Просмотреть все статьи номера >>
Версия для печати
расширенный поискФИНАНСОВОЕ ХРАНИЛИЩЕ ДАННЫХ КОНТУР КОРПОРАЦИЯ
СИСТЕМЫ ДЛЯ БАНКОВ
Финансовое управление банком
Управление филиалами банка
Бюджет хозяйственных расходов
СИСТЕМЫ ДЛЯ ПРЕДПРИЯТИЙ
Контроль финансов холдинга
Бюджет холдинга
Бюджет хозяйственных расходов
Корпоративная налоговая отчетность
CONTOUR BUSINESS INTELLIGENCE
Contour Publisher
Contour Reporter
ОТЗЫВЫ КЛИЕНТОВ
НОВИКОМБАНКАК БАРС БАНКИНТЕРПРОМБАНКТрансКредитБанкЕврофинансМоснарбанкЗапСибГаз
Все права защищены © Intersoft Lab, 2000-2007В НАЧАЛОКАРТА САЙТАКОНТАКТЫПОСЛАТЬ ПИСЬМО дизайнСоздательразделы
сушильный машина asko
билет балет
стопный пластырь
озеленение
сварочный пост
видеосъемка торжество
аденома предстательный железа
renu multiplus 355мл
пескоструйка
промышленый альпинизм
услуга кострома
газонокосилка stiga
красный площадь гум
три цвета: красный
подводный гидромассаж
операторский центр
вызов водитель
крот-95
культура танго
краска двухкомпонентный
дефектоскопия сварной швов
бахила оптом
деловой костюм
время ярославль
компания доминике
время архангельск
снегоход буран
рассылка корреспонденция
lucent definity
сварочный пост
холодильник уценка
детский лагерь пионер
рефконтейнеры
купить nokia 9300i
электросчетчик сэт
длинный нард
сушильный машина frigidaire
огнестойкий краска
природа охота
купить отвед
тонирование стеклопакетов
электроинструмент метабо
билет балет
крутой компания
купить ножовка
купить элеваторный узел
торговый витрина
врач-гинеколог
производственный тара
промышленный аккумулятор
фирменый цвет
продать кайт
купить ниппель
болен алкоголизмом
индивидуальный банковский ячейка
мва
вал редуктор поворот
внешний антенна
флюрисцентная краска
выделение кислорода
ваза 2114
консультирование организация
грунт
цепной конвейер
заказать микроавтобус
беседка
втулка переходный
серверные корпус консольный переключатель
дмитрий шумок
профессиональный фарфор
nokia 6021 купить
лидо пекарня
билет мхат
здание лмк
гидрант
французский вина
автоматический оповещение
задний зеркало
summer кухонный
штангенциркуль
покраска рчв
время иваново
многотарифные электросчетчик
mobil cut
антенна радиочастотный
центр консультирование
укв радиосвязь
авиа отправка
тренировка память
вечерний платье
получение выписка егрп
ваза 2114
враждебный поглощение
покраска аэротенк
перевод денег
белый кофе
крот dr
помидор купля
гнб
измерительный комплекс к2-79
трубогиб дорном
восстановление бухучета
сушильный машина ardo
детский лагерь пионер
пленка пэ
кулер 478
sharp ar-m205
слимент лифт
рак простата
обрезание
вызов водитель
автоинформатор
фосфорецирующая краска
компания сент-лючии
программа шифрование
надпись кружок
интеллектуальный электросчетчик
клеить 88 люкс
купить айсбест
арманьяк доставка
iridium motorola
организация видеоконференция
бюгельные зубной протез
масло форма
акриловый вставка вкладыш
предохранитель пкн
электрокамин dimplex model magic (sp8)
предохранитель пкн
информационный валаам
ром доставка
трансперсональный психология
госпиталь мэш
газонокосилка stiga
тестоокруглитель ленточный
ваттметр
автономный электроснабжение
облицовка электрокамин
ливнесборные решетка
snr
измеритель сопротивление
магнитно-маркерные доска
узи тошиба
корпоративный обслуживание
вкус цвет
кулер тихий
природа охота
спецобувь оптом
рассылка адрес
беременность род
флаг заказ
дермато-венеролог
покраска рчв
индивидуальный банковский ячейка
ливнесборные решетка
сухой мороженый
серверные корпус консольный переключатель
заказать обед
шарошка алмазный
многотарифные электросчетчик
кулер процессор
datamax
мрт коленный сустав
автоподъемник
лак эмаль
эксимер лазер
детский мир
видеосъемка торжество
кофе колониальный товар
кулер тихий
бордюр обоев
этикетировщик
thuraya
значок медаль
купить широкоугольник
тонировка стекол
иностранный долг
структурный штукатурка
монетница
долг
флажок настольный
штендеры
капсула миаози
слименд лифт
информационный валаам
багетный мастерский
корпоративный хранилище данный спирли
тестоделитель
спб доставка
купить электроэнцефалограф
доставка
доставка хим. реагент
озонатор воздуха
диспетчеризация