如何在 Perl 中对实例方法进行猴子修补?
我正在尝试对 LWP::UserAgent 实例进行猴子修补(duck-punch :-),如下所示:
sub _user_agent_get_basic_credentials_patch {
return ($username, $password);
}
my $agent = LWP::UserAgent->new();
$agent->get_basic_credentials = _user_agent_get_basic_credentials_patch;
这不是正确的语法 - 它会产生:
无法修改非左值子例程 调用 [module] 行 [lineno]。
我记得(来自 Programming Perl),调度查找是基于受祝福的包动态执行的(ref($agent)
,我相信),所以我不确定实例猴子修补如何在不影响受祝福的包的情况下工作。
我知道我可以对 UserAgent 进行子类化,但我更喜欢更简洁的猴子修补方法。 同意成年人,你有什么。 ;-)
I'm trying to monkey-patch (duck-punch :-) a LWP::UserAgent
instance, like so:
sub _user_agent_get_basic_credentials_patch {
return ($username, $password);
}
my $agent = LWP::UserAgent->new();
$agent->get_basic_credentials = _user_agent_get_basic_credentials_patch;
This isn't the right syntax -- it yields:
Can't modify non-lvalue subroutine
call at [module] line [lineno].
As I recall (from Programming Perl), dispatch lookup is performed dynamically based on the blessed package (ref($agent)
, I believe), so I'm not sure how instance monkey patching would even work without affecting the blessed package.
I know that I can subclass the UserAgent
, but I would prefer the more concise monkey-patched approach. Consenting adults and what have you. ;-)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
正如 Fayland Lam 的回答,正确的语法是:
但这是修补(动态范围)整个类,而不是只是实例。 就你的情况而言,你可能可以逃脱惩罚。
如果您确实只想影响实例,请使用您描述的子类化。 这可以“即时”完成,如下所示:
As answered by Fayland Lam, the correct syntax is:
But this is patching (dynamically scoped) the whole class and not just the instance. You can probably get away with this in your case.
If you really want to affect just the instance, use the subclassing you described. This can be done 'on the fly' like this:
如果动态范围(使用
local
)不令人满意,您可以自动化自定义包重新祝福技术:示例用法:
If dynamic scope (using
local
) isn't satisfactory, you can automate the custom package reblessing technique:Example usage:
本着 Perl 的“让困难的事情成为可能”的精神,这里有一个示例,说明如何在不破坏继承的情况下进行单实例猴子修补。
我不建议您在任何其他人必须支持、调试或依赖的代码中实际执行此操作(就像您所说的,同意的成年人):
In the spirit of Perl's "making hard things possible", here's an example of how to do single-instance monkey patching without mucking with the inheritance.
I DO NOT recommend you actually doing this in any code that anyone else will have to support, debug or depend on (like you said, consenting adults):
这里你没有 1 个问题,而是 2 个问题,因为这就是你正在做的事情:
在双方的情况下,你都在调用 subs 而不是简单地引用它们。
等效逻辑:
所以它抱怨也就不足为奇了。
出于同样的原因,您的答案中的“固定”代码也是错误的,还有另一个您可能没有意识到的问题:
这是一个相当有缺陷的逻辑思维,它的工作原理就像您想象的那样。
它真正做的事情是:
您根本没有分配任何函数。
当然,猴子修补是相当邪恶的,我自己还没有看到如何在类似的单个实例上重写方法。
但是,您可以做的是:
这将全局替换 get_basic_credentials 代码部分的行为(我可能有点错,有人纠正我)
如果您确实需要在每个实例的基础上执行此操作,您可能可以做一些类继承并只构建一个派生类,和/或动态创建新包。
You have not 1, but 2 problems here, because this is what you are doing:
on both sides cases, you're calling the subs instead of simply referring to them.
Equivalent logic :
So its no wonder its complaining.
Your "fixed" code in your answer is also wrong, for the same reason, with another problem you may not realise:
This is rather flawed logic thinking it works like you think it does.
What it is really doing, is:
You didn't assign any function at all.
Monkey patching is rather evil of course, and I have not myself seen how to override a method on a singular instance of something like that.
However, what you can do is this:
Which will globally replace the get_basic_credentials code sections behaviour ( I might be wrong somewhat, somebody correct me )
If you really need to do it on a per-instance basis you could probably do a bit of class inheritance and just build a derived class instead, and/or dynamically create new packages.
Perl 认为您正在尝试调用赋值左侧的子例程,这就是它抱怨的原因。 我认为你也许可以直接破解 Perl 符号表(使用 *LWP::UserAgent::get_basic_credentials 或其他东西),但我缺乏 Perl-fu 来正确地执行该咒语。
Perl thinks you're trying to call the subroutine on the left of the assignment, which is why it's complaining. I think you may be able to whack the Perl symbol table directly (using
*LWP::UserAgent::get_basic_credentials
or something), but I lack the Perl-fu to correctly make that incantation.基于 John Siracusa 的回答......我发现我仍然想要对原始函数的引用。 所以我这样做了:
它与原始答案相同,只是我传递了一些参数
$self
和$oldFunction
。这让我们像往常一样调用
$self
的$oldFunction
,但在它周围装饰额外的代码。Building upon John Siracusa's answer… I found that I still wanted a reference to the original function. So I did this:
It's the same as in the original answer, except I pass through some parameters
$self
and$oldFunction
.This lets us invoke
$self
's$oldFunction
as usual, but decorate additional code around it.http://www.google.com/codesearch/p?hl=en#tgg5_3LXifM/Authen-Simple-HTTP-0.1/lib/Authen/Simple/HTTP.pm&q=get_basic_credentials
http://www.google.com/codesearch/p?hl=en#tgg5_3LXifM/Authen-Simple-HTTP-0.1/lib/Authen/Simple/HTTP.pm&q=get_basic_credentials
编辑:这是我为后代保留的解决方案的错误尝试。 查看已投票/已接受的答案。 :-)
啊,我刚刚意识到语法需要一点调整:
如果没有
{}
分隔符,它看起来像一个方法调用(这不是有效的左值)。我仍然想知道实例方法如何通过此语法进行绑定/查找。 蒂亚!
Edit: This was an incorrect attempt at a solution that I'm keeping for posterity. Look at the upvoted/accepted answers. :-)
Ah, I just realized that the syntax needs a little bit of adjustment:
Without the
{}
delimiters it looks like a method invocation (which would not be a valid l-value).I'd still like to know how the instance method gets bound/looked up via this syntax. TIA!