Perl方法属性如何工作?
一个鲜为人知的内置Perlfunction是属性。 然而,官方文件在介绍这个概念的新手方面做得相当糟糕。 与此同时,像Catalyst这样的框架广泛使用属性,这似乎使许多事情变得更容易。 由于使用某些东西而不知道这些含义会让我感到厌倦,所以我想知道细节。 语法看起来他们看起来像Python的装饰器,但文档意味着更简单。
你能否解释一下(如果可能,用真实世界的例子)哪些属性是好的,门后面发生了什么?
你是对的,这方面的文档不是很清楚,特别是因为属性不那么复杂。 如果你定义一个子例程属性,像这样:
sub some_method :Foo { }
Perl会在编译程序的时候 (这很重要)在当前包或其父类中查找魔术子MODIFY_CODE_ATTRIBUTES
。 这将使用当前包的名称,对子例程的引用以及为此子例程定义的属性列表来调用。 如果这个处理程序不存在,编译将失败。
你在这个处理程序中做什么完全取决于你。 恩,那就对了。 没有任何隐藏的魔法。 如果您想要发出错误信号,则返回违规属性的名称将导致编译失败并显示“无效属性”消息。
还有一个名为FETCH_CODE_ATTRIBUTES
处理程序,只要有人说,就会被调用
use attributes; my @attrs = attributes::get(\&some_method);
这个处理程序传递了包名和子程序引用,并且应该返回子例程的属性列表(尽pipe你真正要做的是再次由你决定)。
下面是一个例子,可以使用任意属性对方法进行简单的“标记”,您可以稍后查询:
package MyClass; use Scalar::Util qw( refaddr ); my %attrs; # package variable to store attribute lists by coderef address sub MODIFY_CODE_ATTRIBUTES { my ($package, $subref, @attrs) = @_; $attrs{ refaddr $subref } = \@attrs; return; } sub FETCH_CODE_ATTRIBUTES { my ($package, $subref) = @_; my $attrs = $attrs{ refaddr $subref }; return @$attrs; } 1;
现在,在MyClass及其所有子类中,可以使用任意属性,并使用attributes::get()
来查询它们:
package SomeClass; use base 'MyClass'; use attributes; # set attributes sub hello :Foo :Bar { } # query attributes print "hello() in SomeClass has attributes: ", join ', ', attributes::get(SomeClass->can('hello')); 1; __END__ hello() in SomeClass has attributes: Foo, Bar
总而言之,属性并没有做太多的工作,而另一方面却使得它们非常灵活:你可以把它们作为真正的“属性”来使用(如本例所示),实现类似于装饰器的东西(参见思南的答案 )自己狡猾的目的。
$ perl-> buzzbuild议Mike Friedman的文章 。
属性是如果你不知道如何使用它们的事情之一,你不应该打扰他们。 我曾经做过一个database_method
属性,向系统表明在进入这个方法之前需要一个logging集,并且这个方法知道它的主要input来自它所对应的存储过程。
我正在使用属性来包装实际的,指定的行动与该数据。 所以,其中一个看起来很有用的想法是间接地包装方法,但是要让调用者工作,而不是压倒一切。 最后,作为一个“专家专用”function,它显得太明显了,如果你在perl-shop中编写perl,那么需要通过神秘的内部进行跟踪 – 这是你想要避免的。
人们可能想把我投票给我,但是我从思南引用的文章中看到:
注意事项
虽然这是一个强大的技术,但并不完美。 代码不会正确地包装匿名子例程 , 它不一定将调用上下文传播到包装函数 。 此外,使用这种技术将大大增加程序在运行时必须执行的子例程调度的次数。 根据程序的复杂程度,这可能会显着增加调用堆栈的大小。 如果盲目的速度是一个主要的devise目标,这个策略可能不适合你。
除非你愿意覆盖caller
否则这些都是显着的缺点。 我并不关心“盲目的速度”,而且我也乐于尝试重写caller
绕过任何注册自己为“DO_NOT_REPORT”的子程序 – 但是我有一些编码的愚蠢,也没有被殴打过。
即使这篇文章也承认这个function是多么的不健全,并且包含了这个警告。 告诉我什么时候使用一个时髦,晦涩的function是一个好主意? 通常情况下,人们最终会放入UNIVERSAL
命名空间以避免inheritance问题。
(但是,如果你认为这是一个不好的答案,只是另一个downvote会给我一个同行的压力徽章:D)