Spec-Zone.ru › Perl 5.32

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 Порядок разрешения
    • 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.

ОБЗОР

Можно изменить порядок разрешения методов данного класса, используя 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, однако, создает следующий порядок: (D, B, C, A), который не имеет этой проблемы.

Этот пример довольно тривиален; для более сложных случаев и более глубокого объяснения см. ссылки в разделе "СМОТРИТЕ ТАКЖЕ".

Функции

mro::get_linear_isa($classname[, $type])

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

Обычно его используют так:

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

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

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

Вот как он решает, какой фактический метод вызывать:

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

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

  3. Наконец, он ищет по списку MRO C3 до тех пор, пока не достигнет контекстно-включающего класса, затем ищет дальше по списку 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);

СМОТРИТЕ ТАКЖЕ

Оригинальная статья Дилана

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 Стевана Литтла

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

Spec-Zone.ru

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