Spec-Zone.ru › Perl 5.36

mro

СОДЕРЖАНИЕ

  • НАЗВАНИЕ
  • СИНТАКСИС
  • ОПИСАНИЕ
  • ОБЗОР
  • Порядок разрешения C3
    • Что такое C3?
    • Как работает C3
  • Функции
    • mro::get_linear_isa($classname[, $type])
    • mro::set_mro ($classname, $type)
    • mro::get_mro($classname)
    • mro::get_isarev($classname)
    • mro::is_universal($classname)
    • mro::invalidate_all_method_caches()
    • mro::method_changed_in($classname)
    • mro::get_pkg_gen($classname)
    • next::method
    • next::can
    • maybe::next::method
  • СМОТРИТЕ ТАКЖЕ
    • Оригинальная статья о Dylan
    • Python 2.3 MRO
    • Class::C3
  • АВТОР

НАЗВАНИЕ

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 Class::C3, и большая часть документации C3 здесь взята непосредственно оттуда.

Что такое C3?

C3 - это алгоритм, который направлен на обеспечение разумного порядка разрешения методов при множественном наследовании. Он был впервые представлен в языке Dylan (см. ссылки в разделе "СМОТРИТЕ ТАКЖЕ"), а затем был принят как предпочтительный порядок разрешения методов (Method Resolution Order, MRO) для новых классов в Python 2.3. Недавно он был принят в качестве "канонического" порядка разрешения методов для классов Raku.

Как работает 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)

Устанавливает порядок разрешения методов (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 для обеспечения лучшей согласованности в ситуациях множественного наследования. Обратите внимание, что, хотя наследование в целом следует тому порядку разрешения методов, который действует для данного класса, next::method использует только порядок разрешения методов C3.

В общем случае его используют так:

sub some_method {
  my $self = shift;
  my $superclass_answer = $self->next::method(@_);
  return $superclass_answer + 1;
}

Обратите внимание, что вы не (пере)указываете имя метода. Это вынуждает вас всегда использовать то же имя метода, что и у метода, с которого вы начали.

Его можно вызывать как для объекта, так и для класса.

Способ, которым он определяет, какой фактический метод вызывать:

  1. Сначала он определяет линейный порядок разрешения методов C3 объекта или класса, на котором он вызывается.

  2. Затем он определяет класс и имя метода контекста, из которого он был вызван.

  3. Наконец, он ищет вниз по списку порядка разрешения методов C3, пока не достигнет контекстуально окружающего класса, а затем ищет дальше вниз по списку порядка разрешения методов C3 следующий метод с тем же именем, что и контекстуально окружающий метод.

Невозможность найти следующий метод приведет к тому, что будет выброшено исключение (см. ниже альтернативы).

Это существенно отличается от поведения SUPER при сложном множественном наследовании. (Это становится очевидным, когда понимаешь, что общие суперклассы в линейных представленных C3 для данного класса и одного из его родителей не всегда будут упорядочены одинаково для обоих).

Предупреждение: Вызов next::method из методов, определенных вне класса:

Существует особый случай использования next::method внутри подпрограммы, созданной в другом модуле, чем тот, из которого она вызывается. Звучит сложно, но на самом деле нет. Вот пример, который не будет работать правильно:

*Foo::foo = sub { (shift)->next::method(@_) };

Проблема возникает, потому что анонимная подпрограмма, присваиваемая переменной *Foo::foo glob, будет отображаться в стеке вызовов как вызванная __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

Python 2.3 MRO

https://www.python.org/download/releases/2.3/mro/

Class::C3

Class::C3

АВТОР

Brandon L. Black, <blblack@gmail.com>

Основано на работе Stevan Little's Class::C3

© 1993–2021 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.36.0/mro

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API