Spec-Zone.ru › Perl 5.38

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 основана на работе модуля 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])

Возвращает ссылку на массив, представляющий линейный порядок разрешения методов данного класса. Использует порядок разрешения методов, действующий для данного класса по умолчанию, или указанный порядок разрешения ($type, если указан как $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;
}

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

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

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

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

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

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

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–2023 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.38.0/mro

Spec-Zone.ru

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