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.
ОБЗОР
Можно изменить порядок разрешения методов заданного класса, используя use mro , как показано в синопсисе, или с помощью функции "mro::set_mro" ниже.
Специальные методы next::method, next::can, и maybe::next::method недоступны, пока данный mro модуль не будет загружен с помощью use или require.
Порядок разрешения методов C3
В дополнение к традиционному по умолчанию порядку разрешения методов Perl (поиск в глубину, называемый DFS здесь), Perl теперь также поддерживает порядок разрешения методов C3. Поддержка C3 в Perl основана на работе модуля Stevan Little Class::C3, и большая часть документации по C3 взята непосредственно оттуда.
Что такое C3?
C3 - это алгоритм, призванный обеспечить разумный порядок разрешения методов при множественном наследовании. Он был впервые представлен в языке Dylan (см. ссылки в разделе "СМОТРИТЕ ТАКЖЕ"), а затем был принят в качестве предпочтительного порядка разрешения методов (Method Resolution Order) для новых классов в Python 2.3. В последнее время он был принят в качестве "канонического" порядка разрешения методов для классов Perl 6 и по умолчанию для объектов 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])
Возвращает ссылку на массив, являющийся линейным порядком разрешения методов заданного класса. Использует порядок разрешения методов, действующий по умолчанию для этого класса, или указанный порядок разрешения (либо c3 , либо dfs , если указано $type).
Линейный порядок разрешения методов класса представляет собой упорядоченный массив всех классов, которые будут просматриваться при разрешении метода в этом классе, начиная с самого класса.
Если запрашиваемый класс еще не существует, эта функция все равно успешно выполнится и вернёт [ $classname ].
Обратите внимание, что UNIVERSAL (и любые члены UNIVERSAL в порядке разрешения методов) не являются частью порядка разрешения методов класса, даже если все классы неявно наследуют методы от UNIVERSAL и его родителей.
mro::set_mro ($classname, $type)
Устанавливает порядок разрешения методов заданного класса на значение аргумента $type (либо c3 , либо dfs).
mro::get_mro($classname)
Возвращает порядок разрешения методов данного класса (либо 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 для лучшей согласованности в ситуациях множественного наследования. Обратите внимание, что хотя наследование в целом следует порядку разрешения методов, действующему для данного класса, next::method использует только порядок разрешения методов C3.
Обычно его используют так:
sub some_method {
my $self = shift;
my $superclass_answer = $self->next::method(@_);
return $superclass_answer + 1;
} Обратите внимание, что вы не (пере)указываете имя метода. Это вынуждает вас всегда использовать то же имя метода, что и в исходном методе.
Его можно вызывать на объекте или классе, конечно.
Вот как он определяет, какой метод вызвать:
-
Сначала определяется линейный порядок разрешения методов C3 объекта или класса, на котором он вызывается.
-
Затем определяется класс и имя метода контекста, из которого он был вызван.
-
Наконец, он ищет по списку порядка разрешения методов C3 до тех пор, пока не достигнет контекстно окружающего класса, а затем ищет дальше по списку порядка разрешения методов для следующего метода с таким же именем, как у контекстно окружающего метода.
Если следующий метод не найден, будет выброшено исключение (см. ниже альтернативы).
Это существенно отличается от поведения 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
Прототип Perl 6 Object Model Pugs использует 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
АВТОР
Брэндон Л. Блэк, <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.28.3/mro