理解use_require_do使用方法


当使用use, require, do后,那些被成功加载和编译后的文件或模块信息会被存进%INC这个哈希中。在使用use或者require加载文件或模块之前,如果其存在于%INC中,那么对于这些文件或者模块的加载的编译的步骤就被省略掉了。但是如果使用do,它会执行一个无条件的加载,不会去查看文件或者模块在不 在%INC中。同样也可以再BEGIN代码块中对%INC进行操作。在Perl中的.pm后缀表示perl module。在perl 第4版的时候,使用的是.pl后缀,代表的是perl library。但现在,模块、包都使用.pm替代,而.pl主要代表perl程序。
阅读本文前,建议先看一下文章:查看Perl模块安装路径
require
require是用来读取一个包含Perl代码的文件来对他进行编译。如果%INC中已经存在了这个文件的话,require除了返回以外不做什么了。如果没用,那么就加载,然后编译,最后存进%INC。如果require 得到的是一个完整路径的参数的话,reuqire就会试着去读取这个参数指定的文件。即:
1、require用于载入module或perl程序(.pm后缀可以省略,但.pl必须有)
2、require在运行时载入(验证)module
require “/home/hto/test.pl”
如果require 得到的是一个相对路径的参数的话,reuqire就会去@INC中查找
require “test.pl”
如果在查找的路径里有多个文件重名,那么Perl只会使用最先找到的那一个,值得注意的是,被加载的文件或者模块必须返回TRUE。传统上我们会在代码的最后加上”1;”。另外,如果文件时一个模块的话,我们必须使用:
require htolib::test
这条语句相当于:
require “htolib/test.pm”
如果文件或者模块加载失败的话,那么可能的情况有两种:
1、文件找不到
2、文件没有返回TRUE
这时后程序会终止,Perl中意味着执行die,这种错误时可以通过eval被捕捉的。用eval捕捉的一个优点是,我们可以让程序继续运行而不必因为文件或者模块的加载问题而退出,不然程序在报错后自动退出:
eval { require "/file/that/does/not/exists"};
if ($@) {
print "Failed to load, because : $@"
}
如果使用require 'filename'或者require "filename"来包含文件的话,使用方法和do完全近似;
如果使用require Module的话,则需要定义Module.pm的文件而且文件中的package要以Module来命名该模块。
参看:http://perldoc.perl.org/functions/require.html
use
use和reuqire类似,但是use只能作用于模块,也就是pm文件。另外,use发生在complie time,即:
1、use用于载入module
2、use在编译时载入(验证)module
use htolib::Show
这里,use做了两件事:
把语句中::替换成系统各个目录间的分隔符,通常情况下是”/”。
在最后加上.pm。
所以上面例子会变成Show.pm
use()和下面的语句有着同样效果
BEGIN{
require Moduel;
Module->import(LIST);
}
从上面代码可以看到,use首先会条用require,在require完成加载模块之后,import方法会被调用。如果以”()”作为use的第二个参数的话,那么import方法不会被调用。
这里有些例子,他们都完成相同的工作。
use MyModule;
BEGIN {require MyModule; MyModule->import; }
use MyModule qw(foo bar);
BEGIN {require MyModule; MyModule->;import(“foo”,”bar”); }
use MyModule ();
BEGIN {require MyModule; }
这里的import方法不是perl内置的方法,而是一个被调用模块的静态方法。这样听上去,似乎自己写模块是还有编写import这个方法,感觉挺复杂,实际上做起来要比听上去简单许多。
use引入模块的同时,也引入了模块的子模块。而require则不能引入;use还调用了import静态函数,告诉包哪些特征将要被导入当前包中(换一种说法是,use等价于BEGIN{require;import})。
在内部,use其实调用了require和import静态函数。import()函数告诉包哪些特征将要被导入当前包中,这意味着用之前不必验证函数或者变量是否合法,而require是不会调用import()的。use引入模块的同时,也引入了模块的子模块,而require则不能引入,要在重新声明。
use引用模块时,如果模块名称中包含::双冒号,该双冒号将作为路径分隔符,相当于Unix下的/或者Windows下的\。
参看:http://perldoc.perl.org/functions/use.html
do
do和require很类似,只是do会强制load文件而不会先去检查他是否已经存在于%INC。如果do不能读取文件,那么会返回undef,然后设置$!,报告错误。
如果do能读取文件,但是不能编译文件,那么他返回undef,设置$@,报告错误。
如果编译也成功,do会执行filename中的语句,最终返回最有一个表达式的值。
这里filename需要添加单引号,否则会出错;filename可以为任何后缀的,甚至没有后缀,不要求是pl或者pm等。
参看:http://perldoc.perl.org/functions/do.html
动态加载
动态加载的方法,根据运行时不同状态(变量值)选择载入module,对应的有三种写法:
eval "use $pm_name";
eval "require $pm_name";
require $pm_name.".pm";
可以看到前两种需要借助eval,而require file则是本身支持运行时载入文件。以上三种方式可以在程序的任何块中运行,包括子函数中。
perlmod - Perl modules (packages)
参考:http://perldoc.perl.org/perlmod.html
一般文件名需要和package名称相同,这里为Show;
可以定义变量和函数;
不要忘记1;
以及最后的__END__
文件包含可以提高代码的复用性,在Perl是实现文件包含可以才去两条路:
1、使用do或者require(带引号的)那种方式
2、使用require Module或者use Module的模块方式
两者均可。
use和require的异同
相同:
都可以用来引用module(.PM)。
不同:
1)区别在于USE是在当前默认的@INC里面去寻找,一旦模块不在@INC中的话,用USE是不可以引入的,但是require可以指定路径;
2)use引入的名称不需要后缀名,默认找.PM文件,而require需要后缀名,且后缀名可以为.PM或.PL;
Perl代码复用与包的使用总结
本节转自骏马金龙的博客空间,感谢原作者。
函数复用:eval和do执行perl文件
当定义了一个功能比较通用的子程序,比如获取数值的绝对值。想要到处使用这个子程序,就得不断复制、粘贴这段绝对值函数的定义文本。显然这是不太理想的方式。于是就将包含这个子程序的代码放进一个perl文件,然后通过特殊的语法去导入这个文件。例如,文件sum.pm包含一个sum子程序,该子程序返回参数相加之和。
use v5.16;
our $name="FreeOA"; # 全局变量属性
#
sub sum {
my $sum=0;
$sum += $_ for @_;
return $sum;
}
eval语句导入文件
可以在其它perl文件中(如eval.pm)通过eval语句临时编译这个文件(sum.pm)中的语句。只不过在eval之前,需要先将sum.pm文件中的内容读取:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
open my $fh,"<","sum.pm" or die "Can't open file: $!";
undef $/;
my $sum_code=<$fh>; # 读取代码保存到变量中
close $fh;
eval $sum_code; # eval评估编译这段代码,并执行
die $@ if $@;
my $sum=sum(1,2,3); # 引用来自sum.pm的函数
say $sum;
上面的代码会报错。因为eval $sum_code是将来自sum.pm中的代码放在当前文件中临时进行编译,这段来自sum.pm的代码已经属于本文件。就等价于:
eval CODE;
所以来自sum.pm中的全局属性$name在当前文件的use strict编译指示下将引发错误。得将sum.pm内容中的$name去掉,或者加上my修饰。
eval中来自sum.pm的代码将能访问它所在代码块的词法变量。
do语句导入文件
也可以通过do语句临时编译这个文件,它将在当前程序(无论do语句是否是在代码块中)引入编译的结果(但除了子程序外的其它属性,由于一般会加上use strict,而导致为未声明的变量不可使用,间接地,所导入的文件中的变量$name将失效)。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
{
do 'sum.pm';
die $@ if $@;
my $sum=sum(1,2,3);
say $sum;
# say $name; # 因为use strict的存在,而报错
}
say sum(2,3,4); # 出了语句块,函数仍有效
注意,do语句是在当前文件中引入变异结果,而不是当前代码块。
do导入文件时如果使用的是相对路径(如do 'sum1.pm'),将搜索@INC,搜索到后将更新%INC保持跟踪。
require导入文件
想象一下,如果在myperl.pm中使用do一次性导入两个文件sum1.pm、sum2.pm:
do 'sum1.pm';
die $@ if $@;
do 'sum2.pm';
die $@ if $@;
假设sum2.pm中也用了do语句导入sum1.pm,这样将会在myperl.pm中多次导入sum1.pm。其实第二次导入是多余的,尽管两次导入的内容是完全一致的,而且如果开启了use warnings,将会发出警告。
使用require语句可以解决这样重定义问题。
require '/perlapp/sum1.pm'; # 要给定正确的路径
require '/perlapp/sum2.pm';
require会在hash结构%INC中跟踪已经成功导入的文件,即使sum2.pm中也有require 'sum1.pm'语句。
为了跟踪是否曾经导入成功,要求所导入的文件最后要返回一个true,一般都会使用数值1作为所导入文件代码的结尾。并非一定是1,也可以是其它值,只要能表示最后这个文件是成功的就行。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum {
my $sum;
$sum = map { $_ + $sum } $@
$name;
}
1; # 最后一行用1表示成功
以下是require相关的几点特性:
require的本质上do语句,do语句的本质是eval
require是在程序运行时执行的
所导入文件中的任何语法错误都回直接die,因此可以省略die $@ if $@
require还能用于要求版本号,例如:require 5.010;
在使用require时,如果使用的是裸词,例如require Foo::Bar;,将搜索@INC中的Foo/Bar.pm文件
如果使用的不是裸词,如下。如果是绝对路径,则按照绝对路径查找,如果是相对路径,将从@INC路径下直接搜索Foo::Bar文件,显然文件一般不会这样命名,将会发出警告。
require "Foo::Bar"; # (双引号的存在)
require $class; #其中$class="Foo::Bar"
require 'myperl.pm';
包名称冲突问题
假如在sum2.pm中使用require导入了一个代码文件sum1.pm:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "sum2: sum()";
}
1;
如果在sum1.pm中也有一个sum子程序:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum {
say "sum1: sum()";
}
1;
那么在运行sum2.pm的时候,将警告子程序重新定义。因为sum2.pm首先编译好自己的sum(),然后在运行期间require导入文件时,又再次定义sum(),将进行覆盖操作:
Subroutine sum redefined at /perlapp/sum1.pm.....
这样的名称冲突问题,通过声明包来解决。
定义包和访问包属性
要定义一个包,只需要加上关键字package即可。
package PKG_NAME [ VERSION_NUM ];
上面的语句用于声明包,可以带上包版本号,例如package pkg1 0.01;。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
package Sum1;
sub sum {
say "pkg:Sum1,sum()";
}
1;
然后其它文件导入sum1.pm后,就可以使用包名::属性的方式访问sum1.pm中的属性,如子程序。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "file: sum2,sum()";
}
sum(); # 访问本文件定义的sum子程序
Sum1::sum(); # 访问包Sum1::sum子程序,括号不能少
1;
除了子程序,包中的其它非词法作用域的属性也能被访问,包括:标量、数组、hash、文件句柄。例如:
$Sum1::name;
@Sum1::arr;
每个文件都至少定义在一个包内,如果没有显式给定package指令,则这个包默认为main包。所以访问本程序文件内自身属性的时候可以使用main:: + 属性的方式。例如在sum2.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "file: sum2,sum()";
}
sum(); # 访问本文件定义的sum子程序
main::sum(); # 等价于上一行直接访问sum()
Sum1::sum(); # 访问包Sum1::sum子程序,括号不能少
1;
一个文件内多个包
一般来说,一个文件只会定义一个包;但允许一个文件定义通过包。如下:
package Pkg1;
...code here belong to Pkg1...
package Pkg2;
...code here belong to Pkg2...
定义多个包时,从包1到包2中间的所有属性都属于包1。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum { # 位于默认的main包
say "pkg:main,sum()";
}
package Sum1; # 第一个包
sub sum { # 位于Sum1包
say "pkg:Sum1,sum()";
}
sum(); # 访问的是Sum1包的sum()
main::sum(); # 访问的是main包的sum()
package Sum2; # 第二个包
sub sum { # 位于Sum2包
say "pkg:Sum2,sum()";
}
sum(); # 访问的是Sum2包的sum()
Sum1::sum(); # 访问的是Sum1包的sum()
main::sum(); # 访问的是main包的sum()
1;
有一些词语名称总是属于main包的:ARGV、ARGVOUT、ENV、INC、SIG、STDERR、STDIN 以及 STDOUT。有些带有特殊标点符号的名称(如$_,$2,$!),它们也全部属于main。
另外,词法变量是不能使用包名访问的,因为使用包访问的属性,都是"全局"属性。所以要在代码块中访问全局属性,可以加上包名:
package Sum1;
our $var="1234";
sub mysub {
my $var;
...$var...; # 访问的是my $var
$Sum1::var; # 访问的是Sum1包中的$var
}
如果将一个包声明放进代码块,则出了代码块的域后就消失:
package Sum1;
{
package main;
sub sum {
say "in main"
}
sum(); # 调用main中的sum
} # 退出代码块,重新回到Sum1包
sub sum {code} # 属于Sum1包的sum子程序
包代码块
从perl 5.12开始,支持包代码块:
use v5.12;
package pkg1 {
...
}
package pkg2 {
...
}
包代码块相当于词法范围:
package Navigation {
my @homeport = (21.283, -157.842); # 属于包
sub get_me_home {
my @homeport; # 声明词法变量
... @homeport ... # 访问的是词法变量
... @Navigation::homeport ... # 访问的是包变量
}
... @homeport ... # 访问的是包变量
}
它等价于:
{
package Navigation;
my @homeport = (21.283, -157.842); # 属于包
sub get_me_home {
my @homeport; # 声明词法变量
... @homeport ... # 访问的是词法变量
... @Navigation::homeport ... # 访问的是包变量
}
... @homeport ... # refers to the package variable
}
阅读本文前,建议先看一下文章:查看Perl模块安装路径
require
require是用来读取一个包含Perl代码的文件来对他进行编译。如果%INC中已经存在了这个文件的话,require除了返回以外不做什么了。如果没用,那么就加载,然后编译,最后存进%INC。如果require 得到的是一个完整路径的参数的话,reuqire就会试着去读取这个参数指定的文件。即:
1、require用于载入module或perl程序(.pm后缀可以省略,但.pl必须有)
2、require在运行时载入(验证)module
require “/home/hto/test.pl”
如果require 得到的是一个相对路径的参数的话,reuqire就会去@INC中查找
require “test.pl”
如果在查找的路径里有多个文件重名,那么Perl只会使用最先找到的那一个,值得注意的是,被加载的文件或者模块必须返回TRUE。传统上我们会在代码的最后加上”1;”。另外,如果文件时一个模块的话,我们必须使用:
require htolib::test
这条语句相当于:
require “htolib/test.pm”
如果文件或者模块加载失败的话,那么可能的情况有两种:
1、文件找不到
2、文件没有返回TRUE
这时后程序会终止,Perl中意味着执行die,这种错误时可以通过eval被捕捉的。用eval捕捉的一个优点是,我们可以让程序继续运行而不必因为文件或者模块的加载问题而退出,不然程序在报错后自动退出:
eval { require "/file/that/does/not/exists"};
if ($@) {
print "Failed to load, because : $@"
}
如果使用require 'filename'或者require "filename"来包含文件的话,使用方法和do完全近似;
如果使用require Module的话,则需要定义Module.pm的文件而且文件中的package要以Module来命名该模块。
参看:http://perldoc.perl.org/functions/require.html
use
use和reuqire类似,但是use只能作用于模块,也就是pm文件。另外,use发生在complie time,即:
1、use用于载入module
2、use在编译时载入(验证)module
use htolib::Show
这里,use做了两件事:
把语句中::替换成系统各个目录间的分隔符,通常情况下是”/”。
在最后加上.pm。
所以上面例子会变成Show.pm
use()和下面的语句有着同样效果
BEGIN{
require Moduel;
Module->import(LIST);
}
从上面代码可以看到,use首先会条用require,在require完成加载模块之后,import方法会被调用。如果以”()”作为use的第二个参数的话,那么import方法不会被调用。
这里有些例子,他们都完成相同的工作。
use MyModule;
BEGIN {require MyModule; MyModule->import; }
use MyModule qw(foo bar);
BEGIN {require MyModule; MyModule->;import(“foo”,”bar”); }
use MyModule ();
BEGIN {require MyModule; }
这里的import方法不是perl内置的方法,而是一个被调用模块的静态方法。这样听上去,似乎自己写模块是还有编写import这个方法,感觉挺复杂,实际上做起来要比听上去简单许多。
use引入模块的同时,也引入了模块的子模块。而require则不能引入;use还调用了import静态函数,告诉包哪些特征将要被导入当前包中(换一种说法是,use等价于BEGIN{require;import})。
在内部,use其实调用了require和import静态函数。import()函数告诉包哪些特征将要被导入当前包中,这意味着用之前不必验证函数或者变量是否合法,而require是不会调用import()的。use引入模块的同时,也引入了模块的子模块,而require则不能引入,要在重新声明。
use引用模块时,如果模块名称中包含::双冒号,该双冒号将作为路径分隔符,相当于Unix下的/或者Windows下的\。
参看:http://perldoc.perl.org/functions/use.html
do
do和require很类似,只是do会强制load文件而不会先去检查他是否已经存在于%INC。如果do不能读取文件,那么会返回undef,然后设置$!,报告错误。
如果do能读取文件,但是不能编译文件,那么他返回undef,设置$@,报告错误。
如果编译也成功,do会执行filename中的语句,最终返回最有一个表达式的值。
这里filename需要添加单引号,否则会出错;filename可以为任何后缀的,甚至没有后缀,不要求是pl或者pm等。
参看:http://perldoc.perl.org/functions/do.html
动态加载
动态加载的方法,根据运行时不同状态(变量值)选择载入module,对应的有三种写法:
eval "use $pm_name";
eval "require $pm_name";
require $pm_name.".pm";
可以看到前两种需要借助eval,而require file则是本身支持运行时载入文件。以上三种方式可以在程序的任何块中运行,包括子函数中。
perlmod - Perl modules (packages)
参考:http://perldoc.perl.org/perlmod.html
一般文件名需要和package名称相同,这里为Show;
可以定义变量和函数;
不要忘记1;
以及最后的__END__
文件包含可以提高代码的复用性,在Perl是实现文件包含可以才去两条路:
1、使用do或者require(带引号的)那种方式
2、使用require Module或者use Module的模块方式
两者均可。
use和require的异同
相同:
都可以用来引用module(.PM)。
不同:
1)区别在于USE是在当前默认的@INC里面去寻找,一旦模块不在@INC中的话,用USE是不可以引入的,但是require可以指定路径;
2)use引入的名称不需要后缀名,默认找.PM文件,而require需要后缀名,且后缀名可以为.PM或.PL;
Perl代码复用与包的使用总结
本节转自骏马金龙的博客空间,感谢原作者。
函数复用:eval和do执行perl文件
当定义了一个功能比较通用的子程序,比如获取数值的绝对值。想要到处使用这个子程序,就得不断复制、粘贴这段绝对值函数的定义文本。显然这是不太理想的方式。于是就将包含这个子程序的代码放进一个perl文件,然后通过特殊的语法去导入这个文件。例如,文件sum.pm包含一个sum子程序,该子程序返回参数相加之和。
use v5.16;
our $name="FreeOA"; # 全局变量属性
#
sub sum {
my $sum=0;
$sum += $_ for @_;
return $sum;
}
eval语句导入文件
可以在其它perl文件中(如eval.pm)通过eval语句临时编译这个文件(sum.pm)中的语句。只不过在eval之前,需要先将sum.pm文件中的内容读取:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
open my $fh,"<","sum.pm" or die "Can't open file: $!";
undef $/;
my $sum_code=<$fh>; # 读取代码保存到变量中
close $fh;
eval $sum_code; # eval评估编译这段代码,并执行
die $@ if $@;
my $sum=sum(1,2,3); # 引用来自sum.pm的函数
say $sum;
上面的代码会报错。因为eval $sum_code是将来自sum.pm中的代码放在当前文件中临时进行编译,这段来自sum.pm的代码已经属于本文件。就等价于:
eval CODE;
所以来自sum.pm中的全局属性$name在当前文件的use strict编译指示下将引发错误。得将sum.pm内容中的$name去掉,或者加上my修饰。
eval中来自sum.pm的代码将能访问它所在代码块的词法变量。
do语句导入文件
也可以通过do语句临时编译这个文件,它将在当前程序(无论do语句是否是在代码块中)引入编译的结果(但除了子程序外的其它属性,由于一般会加上use strict,而导致为未声明的变量不可使用,间接地,所导入的文件中的变量$name将失效)。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
{
do 'sum.pm';
die $@ if $@;
my $sum=sum(1,2,3);
say $sum;
# say $name; # 因为use strict的存在,而报错
}
say sum(2,3,4); # 出了语句块,函数仍有效
注意,do语句是在当前文件中引入变异结果,而不是当前代码块。
do导入文件时如果使用的是相对路径(如do 'sum1.pm'),将搜索@INC,搜索到后将更新%INC保持跟踪。
require导入文件
想象一下,如果在myperl.pm中使用do一次性导入两个文件sum1.pm、sum2.pm:
do 'sum1.pm';
die $@ if $@;
do 'sum2.pm';
die $@ if $@;
假设sum2.pm中也用了do语句导入sum1.pm,这样将会在myperl.pm中多次导入sum1.pm。其实第二次导入是多余的,尽管两次导入的内容是完全一致的,而且如果开启了use warnings,将会发出警告。
使用require语句可以解决这样重定义问题。
require '/perlapp/sum1.pm'; # 要给定正确的路径
require '/perlapp/sum2.pm';
require会在hash结构%INC中跟踪已经成功导入的文件,即使sum2.pm中也有require 'sum1.pm'语句。
为了跟踪是否曾经导入成功,要求所导入的文件最后要返回一个true,一般都会使用数值1作为所导入文件代码的结尾。并非一定是1,也可以是其它值,只要能表示最后这个文件是成功的就行。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum {
my $sum;
$sum = map { $_ + $sum } $@
$name;
}
1; # 最后一行用1表示成功
以下是require相关的几点特性:
require的本质上do语句,do语句的本质是eval
require是在程序运行时执行的
所导入文件中的任何语法错误都回直接die,因此可以省略die $@ if $@
require还能用于要求版本号,例如:require 5.010;
在使用require时,如果使用的是裸词,例如require Foo::Bar;,将搜索@INC中的Foo/Bar.pm文件
如果使用的不是裸词,如下。如果是绝对路径,则按照绝对路径查找,如果是相对路径,将从@INC路径下直接搜索Foo::Bar文件,显然文件一般不会这样命名,将会发出警告。
require "Foo::Bar"; # (双引号的存在)
require $class; #其中$class="Foo::Bar"
require 'myperl.pm';
包名称冲突问题
假如在sum2.pm中使用require导入了一个代码文件sum1.pm:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "sum2: sum()";
}
1;
如果在sum1.pm中也有一个sum子程序:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum {
say "sum1: sum()";
}
1;
那么在运行sum2.pm的时候,将警告子程序重新定义。因为sum2.pm首先编译好自己的sum(),然后在运行期间require导入文件时,又再次定义sum(),将进行覆盖操作:
Subroutine sum redefined at /perlapp/sum1.pm.....
这样的名称冲突问题,通过声明包来解决。
定义包和访问包属性
要定义一个包,只需要加上关键字package即可。
package PKG_NAME [ VERSION_NUM ];
上面的语句用于声明包,可以带上包版本号,例如package pkg1 0.01;。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
package Sum1;
sub sum {
say "pkg:Sum1,sum()";
}
1;
然后其它文件导入sum1.pm后,就可以使用包名::属性的方式访问sum1.pm中的属性,如子程序。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "file: sum2,sum()";
}
sum(); # 访问本文件定义的sum子程序
Sum1::sum(); # 访问包Sum1::sum子程序,括号不能少
1;
除了子程序,包中的其它非词法作用域的属性也能被访问,包括:标量、数组、hash、文件句柄。例如:
$Sum1::name;
@Sum1::arr;
每个文件都至少定义在一个包内,如果没有显式给定package指令,则这个包默认为main包。所以访问本程序文件内自身属性的时候可以使用main:: + 属性的方式。例如在sum2.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
require '/perlapp/sum1.pm';
sub sum {
say "file: sum2,sum()";
}
sum(); # 访问本文件定义的sum子程序
main::sum(); # 等价于上一行直接访问sum()
Sum1::sum(); # 访问包Sum1::sum子程序,括号不能少
1;
一个文件内多个包
一般来说,一个文件只会定义一个包;但允许一个文件定义通过包。如下:
package Pkg1;
...code here belong to Pkg1...
package Pkg2;
...code here belong to Pkg2...
定义多个包时,从包1到包2中间的所有属性都属于包1。
例如在sum1.pm中:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub sum { # 位于默认的main包
say "pkg:main,sum()";
}
package Sum1; # 第一个包
sub sum { # 位于Sum1包
say "pkg:Sum1,sum()";
}
sum(); # 访问的是Sum1包的sum()
main::sum(); # 访问的是main包的sum()
package Sum2; # 第二个包
sub sum { # 位于Sum2包
say "pkg:Sum2,sum()";
}
sum(); # 访问的是Sum2包的sum()
Sum1::sum(); # 访问的是Sum1包的sum()
main::sum(); # 访问的是main包的sum()
1;
有一些词语名称总是属于main包的:ARGV、ARGVOUT、ENV、INC、SIG、STDERR、STDIN 以及 STDOUT。有些带有特殊标点符号的名称(如$_,$2,$!),它们也全部属于main。
另外,词法变量是不能使用包名访问的,因为使用包访问的属性,都是"全局"属性。所以要在代码块中访问全局属性,可以加上包名:
package Sum1;
our $var="1234";
sub mysub {
my $var;
...$var...; # 访问的是my $var
$Sum1::var; # 访问的是Sum1包中的$var
}
如果将一个包声明放进代码块,则出了代码块的域后就消失:
package Sum1;
{
package main;
sub sum {
say "in main"
}
sum(); # 调用main中的sum
} # 退出代码块,重新回到Sum1包
sub sum {code} # 属于Sum1包的sum子程序
包代码块
从perl 5.12开始,支持包代码块:
use v5.12;
package pkg1 {
...
}
package pkg2 {
...
}
包代码块相当于词法范围:
package Navigation {
my @homeport = (21.283, -157.842); # 属于包
sub get_me_home {
my @homeport; # 声明词法变量
... @homeport ... # 访问的是词法变量
... @Navigation::homeport ... # 访问的是包变量
}
... @homeport ... # 访问的是包变量
}
它等价于:
{
package Navigation;
my @homeport = (21.283, -157.842); # 属于包
sub get_me_home {
my @homeport; # 声明词法变量
... @homeport ... # 访问的是词法变量
... @Navigation::homeport ... # 访问的是包变量
}
... @homeport ... # refers to the package variable
}