Spec-Zone.ru › Perl 5.34

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) для классов нового стиля в 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])

Возвращает ссылку на массив, представляющий линейный порядок разрешения методов (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 является специальным случаем. UNIVERSAL (и родительские) списки isarev не включают все существующие классы, хотя все классы фактически являются потомками для целей наследования методов.

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;
}

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

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

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

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

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

  3. Наконец, он ищет по списку 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

Python 2.3 MRO

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

Class::C3

Class::C3

АВТОР

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

Основано на Class::C3 Stevan Little

© 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.34.0/mro

Spec-Zone.ru

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