mro
СОДЕРЖАНИЕ
ИМЯ
mro - Порядок разрешения методов
СИНОПСИС
use mro; # enables next::method and friends globally
use mro 'dfs'; # enable DFS MRO for this class (Perl default)
use mro 'c3'; # enable C3 MRO for this class ОПИСАНИЕ
Пространство имен "mro" предоставляет несколько утилит для работы с порядком разрешения методов и кэшированием методов в целом.
Эти интерфейсы доступны только в Perl 5.9.5 и выше. См. MRO::Compat на CPAN для большей совместимости с предыдущими версиями Perl.
ОБЗОР
Можно изменить порядок разрешения методов (MRO) заданного класса, используя use mro , как показано в синопсисе, или используя функцию "mro::set_mro" ниже.
Специальные методы next::method, next::can, и maybe::next::method недоступны до тех пор, пока этот mro модуль не будет загружен с помощью use или require.
Порядок разрешения C3
В дополнение к традиционному порядку разрешения методов Perl по умолчанию (поиск в глубину, здесь называется DFS), Perl теперь также поддерживает порядок разрешения C3. Поддержка C3 в Perl основана на работе модуля Stevan Little's Class::C3, и большая часть документации по C3 взята оттуда напрямую.
Что такое C3?
C3 - это алгоритм, предназначенный для обеспечения разумного порядка разрешения методов при множественном наследовании. Он был впервые представлен в языке Dylan (см. ссылки в разделе "СМОТРИТЕ ТАКЖЕ"), а затем был принят в качестве предпочтительного порядка разрешения методов (MRO) для новых классов в Python 2.3. В последнее время он был принят в качестве «канонического» MRO для классов Perl 6 и стандартного MRO для объектов Parrot.
Как работает C3
C3 работает, всегда сохраняя локальный порядок приоритета. Это означает, что ни один класс не будет появляться раньше своих подклассов. Например, рассмотрим классическую схему наследования с алмазной структурой:
<A>
/ \
<B> <C>
\ /
<D> Стандартный порядок разрешения методов Perl 5 был бы (D, B, A, C). Результатом является то, что A появляется перед C, даже если C является подклассом A. Алгоритм C3 MRO, однако, генерирует следующий порядок: (D, B, C, A), который не имеет этой проблемы.
Этот пример довольно тривиален; для более сложных случаев и более глубокого объяснения см. ссылки в разделе "СМОТРИТЕ ТАКЖЕ".
Функции
mro::get_linear_isa($classname[, $type])
Возвращает указатель на массив, который является линейным порядком разрешения методов (MRO) заданного класса. Использует используемый по умолчанию MRO для данного класса или указанный MRO (либо c3 или dfs если указано $type).
Линейный MRO класса представляет собой упорядоченный массив всех классов, которые будут просматриваться при разрешении метода в этом классе, начиная с самого класса.
Если запрашиваемого класса еще нет, эта функция все равно будет успешной и вернет [ $classname ]
Обратите внимание, что UNIVERSAL (и любые члены MRO UNIVERSAL) не являются частью MRO класса, даже если все классы неявно наследуют методы от UNIVERSAL и его родителей.
mro::set_mro ($classname, $type)
Устанавливает порядок разрешения методов (MRO) данного класса на значение аргумента $type (либо c3 или dfs).
mro::get_mro($classname)
Возвращает порядок разрешения методов (MRO) данного класса (либо c3 или dfs).
mro::get_isarev($classname)
Возвращает mro_isarev для этого класса в виде массива имен классов. Это все классы, которые «isa» заданному имени класса, даже если отношение isa является косвенным. Это используется внутри кода MRO для отслеживания аннулирования кэшей методов/MRO.
Как и в случае с mro::get_linear_isa выше, UNIVERSAL является специальным. Список isarev UNIVERSAL (и родителей) не включает все существующие классы, даже если все классы фактически являются потомками для наследования методов.
mro::is_universal($classname)
Возвращает булевое значение, указывающее, является ли данное имя класса самим UNIVERSAL или одним из родителей UNIVERSAL по наследованию @ISA.
Любой класс, для которого эта функция возвращает true, является «универсальным» в том смысле, что все классы потенциально наследуют методы от него.
mro::invalidate_all_method_caches()
Увеличивает PL_sub_generation, что приводит к аннулированию кэширования методов во всех пакетах.
mro::method_changed_in($classname)
Аннулирует кэш методов любых классов, зависящих от данного класса. Обычно это не нужно. Единственный известный случай, когда чистый Perl-код может спутать кэш методов, - это когда вы вручную устанавливаете новую константную подпрограмму, используя скалярное значение только для чтения, как это делают внутренности модуля constant. Если вы найдете другой случай, сообщите о нем, чтобы мы либо исправили его, либо добавили сюда документацию по исключению.
mro::get_pkg_gen($classname)
Возвращает целое число, которое увеличивается каждый раз, когда реальный локальный метод в пакете $classname изменяется или изменяется локальный @ISA $classname.
Это предназначено для авторов модулей, которые много делают с интроспекцией классов, так как это позволяет им очень быстро проверить, изменились ли какие-либо важные свойства локальных свойств данного класса с момента последнего просмотра. Оно не увеличивается при изменении методов/@ISA в суперклассах.
Вам все равно нужно найти фактические изменения, и их может и не быть. Возможно, все изменения с момента вашего последнего просмотра взаимно компенсировались и оставили пакет в том состоянии, в котором он был раньше.
Это целое число обычно начинается с значения 1 при создании хранилища пакета. Вызов его для пакетов, чьи хранилища вообще не существуют, вернет 0. Если хранилище пакета полностью удалено (редкий случай, но это может произойти, если кто-то сделает что-то вроде undef %PkgName::), число будет сброшено либо до 0 или 1, в зависимости от того, насколько полностью пакет был удален.
next::method
Это похоже на SUPER, но оно использует порядок разрешения методов C3 для повышения согласованности в ситуациях с множественным наследованием. Обратите внимание, что, хотя наследование в целом следует тому порядку MRO, который действует для данного класса, next::method использует только порядок разрешения C3.
Обычно он используется так:
sub some_method {
my $self = shift;
my $superclass_answer = $self->next::method(@_);
return $superclass_answer + 1;
} Обратите внимание, что вы не (пере)указываете имя метода. Это заставляет вас всегда использовать то же имя метода, что и в методе, с которого вы начали.
Его можно вызвать как для объекта, так и для класса, конечно.
Вот как определяется, какой фактический метод вызвать:
-
Сначала определяется линейный порядок разрешения C3 MRO для объекта или класса, на котором вызывается метод.
-
Затем определяется класс и имя метода контекста, из которого был вызван метод.
-
Наконец, он ищет вниз по списку C3 MRO, пока не достигнет контекстно окружающего класса, затем ищет в списке MRO следующий метод с тем же именем, что и контекстно окружающий метод.
Если следующий метод не найден, будет выброшено исключение (см. альтернативы ниже).
Это существенно отличается от поведения SUPER в сложных ситуациях с множественным наследованием. (Это становится очевидным, когда понимаешь, что общие суперклассы в линейных представлениях C3 данного класса и одного из его родителей не всегда будут упорядочены одинаково для обоих.)
Примечание: Вызов next::method из методов, определенных за пределами класса:
Есть крайний случай, когда используется next::method из подпрограммы, созданной в другом модуле, чем тот, из которого она вызывается. Звучит сложно, но на самом деле нет. Вот пример, который не будет работать правильно:
*Foo::foo = sub { (shift)->next::method(@_) }; Проблема заключается в том, что анонимная подпрограмма, присвоенная глобальной переменной *Foo::foo, будет отображаться в стеке вызовов как вызываемая из __ANON__, а не foo, как можно было ожидать. Так как next::method использует caller для определения имени метода, в котором она была вызвана, это приведет к ошибке в данном случае.
Но не волнуйтесь, есть простое решение. Модуль Sub::Name позволит проникнуть во внутренности Perl и назначить имя анонимной подпрограмме. Просто сделайте так:
use Sub::Name 'subname';
*Foo::foo = subname 'Foo::foo' => sub { (shift)->next::method(@_) }; и все будет работать.
next::can
Это похоже на next::method, но просто возвращает либо ссылку на код, либо undef для указания того, что больше методов с таким именем не существует.
maybe::next::method
В простых случаях, это эквивалентно:
$self->next::method(@_) if $self->next::can; Но существуют случаи, когда только это решение работает (например, goto &maybe::next::method);
ССЫЛКИ
Оригинальная статья о Dylan
- http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.19.3910&rep=rep1&type=pdf
Pugs
Прототип Pugs модели объектов Perl 6 использует C3
Parrot
Parrot теперь использует C3
- http://use.perl.org/~autrijus/journal/25768
Ссылки по теме MRO Python 2.3
- http://www.python.org/2.3/mro.html
- http://www.python.org/2.2.2/descrintro.html#mro
Class::C3
- Class::C3
АВТОР
Brandon L. Black, <blblack@gmail.com>
Основано на Class::C3 Стивена Литтла
© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.30.3/mro