内容

名称

attributes - 获取/设置子程序或变量属性

概要

sub foo : method ;
my ($x,@y,%z) : Bent = 1;
my $s = sub : method { ... };

use attributes ();	# optional, to get subroutine declarations
my @attrlist = attributes::get(\&foo);

use attributes 'get'; # import the attributes::get subroutine
my @attrlist = get \&foo;

描述

子程序声明和定义可以选择性地与属性列表相关联。(变量 my 声明也可以,但请参见下面的警告。)Perl 通过将有关调用站点和声明内容的一些信息与属性列表一起传递给此模块来处理这些声明。特别是,上面的第一个示例等效于以下内容

use attributes __PACKAGE__, \&foo, 'method';

概要中的第二个示例执行与以下内容等效的操作

use attributes ();
my ($x,@y,%z);
attributes::->import(__PACKAGE__, \$x, 'Bent');
attributes::->import(__PACKAGE__, \@y, 'Bent');
attributes::->import(__PACKAGE__, \%z, 'Bent');
($x,@y,%z) = 1;

是的,这扩展了很多。

警告:变量的属性声明仍在不断发展中。此类声明的语义和接口可能会在未来的版本中发生变化。它们的存在是为了实验这些语义应该是什么。不要依赖于此功能的当前实现。

目前只有少数属性由 Perl 本身(或直接由此模块处理,具体取决于您的看法)。但是,包特定的属性可以通过扩展机制允许。(参见下面的“包特定的属性处理”)。

子程序属性的设置发生在编译时。our 声明中的变量属性也在编译时应用。但是,my 变量在运行时应用其属性。这意味着您必须到达my 的运行时组件,这些属性才会被应用。例如

my $x : Bent = 42 if 0;

既不会将 42 赋值给 $x,也不会Bent 属性应用于该变量。

尝试设置一个无法识别的属性会导致致命错误。(该错误是可以捕获的,但它仍然会停止该eval 中的编译。)设置一个名称全部为小写字母且不是内置属性(例如“foo”)的属性会导致警告,并带有 -wuse warnings 'reserved'

import 的作用

在描述中提到

sub foo : method;

等效于

use attributes __PACKAGE__, \&foo, 'method';

如您所知,这会在编译时使用以下参数调用 attributesimport 函数:“attributes”、调用者的包名、代码的引用和“method”。

attributes->import( __PACKAGE__, \&foo, 'method' );

所以您想知道 import 到底做了什么?

首先,import 获取第三个参数的类型(在本例中为“CODE”)。attributes.pm 检查调用者的命名空间(此处为“main”)中是否存在名为 MODIFY_<reftype>_ATTRIBUTES 的子程序。在本例中,需要一个子程序 MODIFY_CODE_ATTRIBUTES。然后调用此方法来检查您是否使用了“错误属性”。此示例中的子程序调用将类似于

MODIFY_CODE_ATTRIBUTES( 'main', \&foo, 'method' );

MODIFY_<reftype>_ATTRIBUTES 必须返回所有“错误属性”的列表。如果存在任何错误属性,import 会报错。

(参见下面 "包特定属性处理" )

内置属性

以下是子程序的内置属性

lvalue

表示引用的子程序是有效的左值,可以赋值。子程序必须返回可修改的值,例如标量变量,如 perlsub 中所述。

此模块允许您在已定义的子程序上设置此属性。对于 Perl 子程序(XSUB 也可以),它可能或可能不会按您预期的方式工作,具体取决于子程序内部的代码,细节可能会在未来的 Perl 版本中发生变化。您可能会遇到左值上下文未正确传播到子程序中的问题,甚至可能出现断言失败。因此,如果启用了警告,则会发出警告。换句话说,您应该只在您真正知道自己在做什么的情况下执行此操作。您已被警告。

method

表示引用的子程序是一个方法。标记为该属性的子程序不会触发“歧义调用解析为 CORE::%s”警告。

prototype(..)

“prototype”属性是指定子程序原型的一种替代方法。所需的原型在括号内。

属性中的原型在子程序原型之后立即分配给子程序,这意味着如果两者同时声明,则传统定义的原型将被忽略。换句话说,sub foo($$) : prototype(@) {}sub foo(@){} 没有区别。

如果启用了 illegalproto 警告,则此属性中声明的原型将在编译时进行完整性检查。

const

此实验性属性在 Perl 5.22 中引入,仅适用于匿名子程序。它会导致子程序在 sub 表达式求值时立即被调用。返回值被捕获并转换为常量子程序。

以下是变量的内置属性

shared

表示引用的变量可以在与 threadsthreads::shared 模块结合使用时跨不同线程共享。

可用子程序

加载此模块后,以下子程序可供一般使用

get

此例程需要一个参数——对子程序或变量的引用。它返回一个属性列表,该列表可能为空。如果传递无效参数,它将使用 die()(通过 Carp::croak)引发致命异常。如果它可以为类方法查找找到合适的包名,它将包含来自 FETCH_type_ATTRIBUTES 调用的结果在其返回列表中,如以下 "包特定属性处理" 中所述。否则,将仅返回 内置属性

reftype

此例程需要一个参数——对子程序或变量的引用。它返回引用变量的内置类型,忽略它可能被祝福的任何包。这对于确定构成以下 "包特定属性处理" 中描述的方法名称一部分的 type 值很有用。

请注意,这些例程不会默认导出。

包特定属性处理

警告:此处描述的机制仍在试验阶段。不要依赖当前的实现。特别是,没有规定将包属性应用于用作闭包的子程序的“克隆”副本。(有关闭包的信息,请参阅 "perlref 中的制作引用"。)包特定属性处理可能会在将来的版本中发生不兼容的更改。

当属性列表出现在声明中时,会检查是否在相应的包(或其 @ISA 继承树)中存在属性“修改”处理程序。类似地,当对有效引用调用 attributes::get 时,会检查是否存在相应的属性“获取”处理程序。请参阅 "示例" 以了解“适当包”确定是如何工作的。

处理程序名称基于正在声明的变量或传递的引用的底层类型。由于这些属性与子程序或变量声明相关联,因此它故意忽略了被祝福到某个包中的任何可能性。因此,子程序声明使用“CODE”作为其 type,即使是祝福的哈希引用也使用“HASH”作为其 type

用于修改和获取的类方法如下

FETCH_type_ATTRIBUTES

此方法使用两个参数调用:相关包名和对需要包定义属性的变量或子程序的引用。预期的返回值是关联属性的列表。此列表可能为空。

MODIFY_type_ATTRIBUTES

此方法使用两个固定参数调用,然后是来自相关声明的属性列表。两个固定参数是相关包名和对声明的子程序或变量的引用。预期的返回值是此处理程序未识别的属性列表。请注意,这允许派生类将调用委托给其基类,然后仅检查基类尚未为其处理的属性。

对该方法的调用目前是在声明处理过程中进行的。特别是,这意味着子例程引用可能指向一个未定义的子例程,即使该声明实际上是定义的一部分。

在空包声明 package ; 的范围内,对未祝福的变量引用调用 attributes::get() 不会为“获取”方法查找提供任何起始包名称。因此,这种情况不会导致对包定义的属性进行方法调用。命名子例程知道它属于(或最初属于)哪个符号表条目,并将使用相应的包。匿名子例程知道它被编译到的包名称(除非它也被编译为一个空包声明),因此它将使用该包名称。

属性列表的语法

属性列表是由空格或冒号(可选空格)分隔的属性规范序列。每个属性规范都是一个简单名称,可选地后跟一个带括号的参数列表。如果存在这样的参数列表,则会根据 q() 运算符的规则对其进行扫描。(参见 "perlop 中的引号和类似引号的运算符"。)但是,参数列表将按原样传递,而不是按 q() 的方式传递。

语法上有效的属性列表的一些示例

switch(10,foo(7,3))  :  expensive
Ugly('\(") :Bad
_5x5
lvalue method

语法上无效的属性列表的一些示例(带注释)

switch(10,foo()		# ()-string not balanced
Ugly('(')			# ()-string not balanced
5x5				# "5x5" not a valid identifier
Y2::north			# "Y2::north" not a simple identifier
foo + bar			# "+" neither a colon nor whitespace

导出

默认导出

无。

可用的导出

getreftype 例程是可导出的。

定义的导出标签

:ALL 标签将获取所有上述导出。

示例

以下是一些语法上有效的声明示例,并注释了它们如何在内部解析为 perl 中的 use attributes 调用。这些示例主要用于了解如何为包定义的属性的可能方法查找找到“适当的包”。

  1. 代码

    package Canine;
    package Dog;
    my Canine $spot : Watchful ;

    效果

    use attributes ();
    attributes::->import(Canine => \$spot, "Watchful");
  2. 代码

    package Felis;
    my $cat : Nervous;

    效果

    use attributes ();
    attributes::->import(Felis => \$cat, "Nervous");
  3. 代码

    package X;
    sub foo : lvalue ;

    效果

    use attributes X => \&foo, "lvalue";
  4. 代码

    package X;
    sub Y::x : lvalue { 1 }

    效果

    use attributes Y => \&Y::x, "lvalue";
  5. 代码

    package X;
    sub foo { 1 }
    
    package Y;
    BEGIN { *bar = \&X::foo; }
    
    package Z;
    sub Y::bar : lvalue ;

    效果

    use attributes X => \&X::foo, "lvalue";

最后一个示例纯粹是为了完整性。你不应该试图修改不在你自己的包中的东西的属性。

更多示例

  1. sub MODIFY_CODE_ATTRIBUTES {
       my ($class,$code,@attrs) = @_;
    
       my $allowed = 'MyAttribute';
       my @bad = grep { $_ ne $allowed } @attrs;
    
       return @bad;
    }
    
    sub foo : MyAttribute {
       print "foo\n";
    }

    此示例运行。在编译时调用 MODIFY_CODE_ATTRIBUTES。在该子例程中,我们检查是否有任何属性被禁止,并返回这些“错误属性”的列表。

    由于我们返回一个空列表,因此一切正常。

  2. sub MODIFY_CODE_ATTRIBUTES {
       my ($class,$code,@attrs) = @_;
    
       my $allowed = 'MyAttribute';
       my @bad = grep{ $_ ne $allowed }@attrs;
    
       return @bad;
    }
    
    sub foo : MyAttribute Test {
       print "foo\n";
    }

    此示例在编译时中止,因为我们使用了“Test”属性,该属性是不允许的。MODIFY_CODE_ATTRIBUTES 返回一个包含单个元素('Test')的列表。

另请参阅

有关基本声明的详细信息,请参见 "perlsub 中的通过 my() 的私有变量""perlsub 中的子例程属性";有关正常调用机制的详细信息,请参见 "perlfunc 中的 use"