Можете ли вы перехватить вызов метода в Perl, сделать что-то с аргументами, а затем выполнить его?Можно ли перехватить вызовы метода Perl?
ответ
Да, вы можете перехватить вызовы подпрограммы Perl. У меня есть целая глава о таких вещах в Mastering Perl. Проверьте модуль Hook::LexWrap, который позволяет вам делать это, не просматривая все детали. Методы Perl - это просто подпрограммы.
Вы также можете создать подкласс и переопределить метод, который хотите поймать. Это немного лучший способ сделать это, потому что именно так хочет объектно-ориентированное программирование. Однако иногда люди пишут код, который не позволяет вам делать это правильно. Об этом в Mastering Perl.
Это выглядит как работа для Moose! Moose - это объектная система для Perl, которая может это сделать и многое другое. docs будет намного лучше объяснять, чем я могу, но то, что вы, скорее всего, захотите, это Method Modifier, в частности before
.
Moose - это то, что вы используете для всего приложения, а не для целенаправленных проблем. –
Но это зависит от того, в чем проблема, и у задающегося вопросом вопроса нет никаких подробностей. Возможно, кто-то оценит подход, который следует предпринять при создании нового приложения, и в этом случае его включение в Муз будет подходящим и полезным ответом. – jsoverson
Мы только что сказали то же самое. Я не сказал, чтобы не использовать Муса, но я не сказал, чтобы использовать его. –
Чтобы кратко описать, Perl обладает способностью изменять таблицу символов. Вы вызываете подпрограмму (метод) через таблицу символов пакета, к которой принадлежит метод. Если вы измените таблицу символов (и это не считается очень грязным), вы можете заменить большинство вызовов методов вызовом других методов, которые вы указали. Это демонстрирует подход:
# The subroutine we'll interrupt calls to
sub call_me
{
print shift,"\n";
}
# Intercepting factory
sub aspectate
{
my $callee = shift;
my $value = shift;
return sub { $callee->($value + shift); };
}
my $aspectated_call_me = aspectate \&call_me, 100;
# Rewrite symbol table of main package (lasts to the end of the block).
# Replace "main" with the name of the package (class) you're intercepting
local *main::call_me = $aspectated_call_me;
# Voila! Prints 105!
call_me(5);
Это также показывает, что, когда кто-то берет ссылку подпрограммы и называет его по ссылке, вы больше не можете влиять на такие вызовы.
Я уверен, что существуют рамки для аксессуаров в perl, но это, я надеюсь, демонстрирует подход.
Да.
Вам нужны три вещи:
аргументы для вызова в @_
, который является еще одним динамической областью видимости переменной.
Затем goto
поддерживает аргумент reference-sub, который сохраняет текущий @_
, но выполняет другой (хвостовой) вызов функции.
Окончательно local
может использоваться для создания глобальных переменных с лексической границей, а таблицы символов похоронены в %::
.
Итак, вы получили:
sub foo {
my($x,$y)=(@_);
print "$x/$y = " . ((0.0+$x)/$y)."\n";
}
sub doit {
foo(3,4);
}
doit();
, которые, конечно, распечатывает:
3/4 = 0.75
Мы можем заменить Foo с помощью local
и перейти:
my $oldfoo = \&foo;
local *foo = sub { (@_)=($_[1], $_[0]); goto $oldfoo; };
doit();
И теперь мы получаем :
4/3 = 1.33333333333333
Если вы хотите изменить *foo
без использования его имени, и вы не хотите использовать eval
, то вы можете изменить его, манипулируя %::
, например:
$::{"foo"} = sub { (@_)=($_[0], 1); goto $oldfoo; };
doit();
И теперь мы получаем:
3/1 = 3
Вы можете, и Павел описывает хороший способ сделать это, но вам, вероятно, следует уточнить, почему вы хотите сделать это в первую очередь.
Если вы ищете расширенные способы перехвата вызовов произвольным подпрограммам, то работа с таблицами символов будет работать для вас, но если вы хотите добавить функциональность к функциям, возможно, экспортированным в пространство имен, в котором вы сейчас работаете, то вам может понадобиться знать способы вызова функций, которые существуют в других пространствах имен.
Данные: например, Dumper экспортирует функцию «Dumper» в вызывающее пространство имен, но вы можете переопределить или отключить ее и предоставить свою собственную функцию Dumper, которая затем вызывает оригинал с полным именем.
например.
use Data::Dumper;
sub Dumper {
warn 'Dumping variables';
print Data::Dumper::Dumper(@_);
}
my $foo = {
bar => 'barval',
};
Dumper($foo);
Опять же, это альтернативное решение, которое может быть более подходящим в зависимости от исходной проблемы. При игре с таблицей символов можно весело провести время, но это может быть чрезмерным и может привести к сложному поддержанию кода, если он вам не нужен.
В этом случае вы должны дать пустой список импорта Dumper, чтобы вы ничего не импортировали. Кроме того, последовательность имеет значение в этом случае. Сначала нужно импортировать, затем переопределить. Определяется последнее определение подпрограммы. Наконец, под предупреждениями вы получите сообщение об ошибке «redefine». –
Определенно, просто стараюсь, чтобы это было просто для примера. Прямое имеет большую ценность (и быстрее для ответчика), чем объяснение каждой касательной детали. Концепции импорта и предупреждения лучше всего будут объяснять в качестве других вопросов. – jsoverson
Я вижу, что вы использовали тег «aop». Почему бы не спросить о конкретных методах АОП, которые вы хотели бы использовать? (Ответы на ваш вопрос могут помочь вам внедрить систему АОП, но зачем это делать, когда кто-то еще это сделал?) – jrockway
Я думал, что это можно сделать с помощью атрибутов метода, но теперь я не могу найти соответствующую страницу perldoc (perldoc-атрибуты это не та страница, о которой я думал). Я вспоминаю хороший пример, включающий в себя обертывание различных методов для добавления в журнал ... – Ether
См. Этот связанный вопрос SO для дополнительной информации: http://stackoverflow.com/questions/635212/how-i-redefine-perl-class-methods – draegtun