2010-11-27 2 views
0

Можно создать дубликат:
In Perl, how can I check from which module a given function was imported?Как я могу определить, к какому пакету принадлежит метод Perl?

Я работаю с некоторыми унаследованного кода, где более чем один модуль XML разбора которая была use d (!).

Это означает, что методы с тем же именем топтаются друг на друга.

Есть ли простой способ определить, к какому пакету относится данный метод?

+0

@rafl: Thahks для поиска соответствующего вопроса. Закроет этот вопрос. – Zaid 2010-11-27 11:21:12

ответ

1

Простой и, скорее всего, правильный ответ заключается в том, что последним загруженным модулем, который экспортирует заданную функцию, будет тот, чья функция используется.

Когда функция (или другой символ) экспортируется, таблица символов для принимающего пакета изменяется. Поэтому, если две функции импорта вносят изменения в таблицу, последнее изменение - это то, что сохраняется.

Напомним, что use Foo; является не более эквивалентом BEGIN { require Foo; Foo->import if Foo->can(import); }.

С import - это просто подпрограмма со специальным именем, единственным ограничением является искаженное воображение Дж. Случайного хакера. import может быть любой код. Он может делать что угодно или ничего. Например, он может содержать логику, чтобы убедиться, что уже определенные функции не переписаны.

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


Для того, чтобы быть на 100% технически правильно, use Foo; эквивалентно BEGIN { require Foo; Foo->import; }.

За исключением того, что этот код не работает, как вы могли ожидать.

Где точный код отличается от моего примера.

Что делает вышеуказанный код, связано с тем, как работает разрешение метода в Perl.

Обычно, вызов Foo->some_sub, где some_sub не существует и AUTOLOAD функция определена в Foo, some_sub будут обрабатываться с помощью функции AUTOLOAD. Существует специальный случай для import, который освобождает его от AUTOLOAD чеков. См. perlsub on Autoloading.

После того, как AUTOLOAD (на самом деле не был) проверен, мы проверяем наследование. Аналогичная проверка соответствия функций или функция AUTOLOAD повторяется для каждого элемента в исходном пакете @ISA. Это рекурсивный процесс, включающий родителей родителей и т. Д., Пока не будет проверено все дерево наследования. @ISA проверяется слева направо, глубиной первого порядка. Все это время исключение AUTOLOAD на месте, и оно будет пропущено для import.

В конечном счете, если не найдено соответствующего метода, мы возвращаемся к UNIVERSAL универсальному базовому классу. Если функция существует в UNIVERSAL, она вызывается, иначе мы выставляем исключение, в котором говорится, что функция не может быть найдена.

Так, в случае нашего звонка Foo->import;, UNIVERSAL::import вызывается для обработки задания. Итак, что делает эта функция?

В Perl 5.12.2 код выглядит следующим образом:

sub import { 
    return unless $_[0] eq __PACKAGE__; 
    return unless @_ > 1; 
    require warnings; 
    warnings::warnif(
     'deprecated', 
     'UNIVERSAL->import is deprecated and will be removed in a future perl', 
    ); 
    goto &Exporter::import; 
} 

Обратите внимание, что первая вещь, которую функция делает выручать, если он не был вызван на UNIVERSAL.

Теперь все, что я сказал, правда, до тех пор, пока вы не сделать несколько вещей:

  • переопределения разрешающего заказа метода. Если вы это сделаете, то разрешение метода произойдет, но вы определили его. Доходим ли мы до УНИВЕРСАЛЬНОГО или нет или когда полностью в воздухе и подчиняемся вашим прихотям.

  • переопределить UNIVERSAL::import. Вы можете обелить обезьяну UNIVERSAL :: import, чтобы делать все, что захотите. Опять же, как это будет вести себя, вы полностью подчиняетесь своим прихотям.

Таким образом, полуэквивалентный код, приведенный выше, является просто сокращением того, что происходит. Я думал, что это будет легче понять, поскольку это не требует знания как можно большего количества деталей о том, как Perl делает что-то, но это не эквивалентно 100% тому, что на самом деле происходит. Выполнение неожиданных вещей нарушает эквивалентность.

Где мой код зависит от точного кода эквивалентного

Кроме того, мой код вызывает Foo->can который обычно возвращается к UNIVERSAL::can. Нигде нет can, вызванный в нормальной цепочке событий. Это особенно привлекательно, когда вы рассматриваете проблемы с can в Perl.

  • can может быть переопределен или переопределен любым классом в графе наследования. Которое вызываемое can подлежит порядку разрешения метода. Здесь применяются все проблемы с множественным наследованием.
  • can не видит загруженные автозагрузкой функции. Поскольку автозагрузка не применяется к импорту, это может показаться не большой проблемой. Проблема в том, что считается хорошей практикой перегружать can, чтобы учесть это, если вы используете автозагрузку. Таким образом, это связано с проблемами выше. Лучше всего использовать неосновный модуль NEXT, чтобы включить повторную пересылку метода, чтобы можно было обрабатывать каждый модуль в цепочке. К сожалению, это редко делается.

Заключение

Все это чертовски много жевать.

Вы можете принять мою стенографию, зная, что в некоторых случаях это не совсем правильно.

Или вы можете принять фактический код, который имеет свой собственный набор исключений.

В любом случае, если вы пробиваете поверхность любого из примеров, есть некоторые тонкие проблемы, с которыми можно справиться.

+2

Нет необходимости проверять флажок для импорта, импорт UNIVERSAL (который ничего не делает, если не вызван в UNIVERSAL), будет вызван, если в Foo – MkV 2010-11-27 11:43:26

Смежные вопросы