【发布时间】:2013-07-09 04:08:24
【问题描述】:
我有一个 perl mason 文件,其中一行如下所示:
$result = PI::Membership::Service->cancel(name => $name)
这到底是什么意思?它是在调用另一个模块吗?它是面向对象的 perl 代码吗?
谢谢
【问题讨论】:
我有一个 perl mason 文件,其中一行如下所示:
$result = PI::Membership::Service->cancel(name => $name)
这到底是什么意思?它是在调用另一个模块吗?它是面向对象的 perl 代码吗?
谢谢
【问题讨论】:
它正在调用(调用)带有三个参数的子例程PI::Membership::Service::cancel。
"PI::Membership::Service""name"$name给定正常的命名约定,这是在 PI::Membership::Service 包中调用一个名为 cancel 的子例程,该子程序定义在您的 @INC 路径某处名为 PI/Membership/Service.pm 的文件中(但是,有许多异常的命名约定,所以不能保证你会找到这样的文件)。如果PI::Membership::Service 包(类)继承自一个或多个其他包,则cancel 子例程实际上可能定义在其中一个包中。
更多详情请见perlobj。
【讨论】:
use 或require 语句的源代码。
PI::Membership::Service 不继承 cancel 方法。它可以在父类中定义。
除非$result 是Pi::Membership::Service 对象,否则它不是真正的面向对象调用,因为它既不创建也不操作对象。面向对象的调用如下所示:
my $obj = Foo::Bar->new; #Creating an object of class `Foo::Bar`
$obj->Baz #Calling method "Baz" on object "$obj";
看起来这是一个对象样式调用,用于访问位于另一个包中且尚未导出的子例程。
要了解实际情况,您必须了解命名空间。 Perl 使用命名空间。大多数时候您可能没有意识到这一点,因为您使用的是main 的默认命名空间。命名空间是必需的,因为您最终可能会(尤其是在 Perl 的 4.x 之前的版本中)函数和变量名称冲突。这是我用旧的 Perl 3.x 风格编写的程序:
require "fribulate.pl";
$value = 4.5;
$new_value = fribulate($value);
print "Fribulated Value = $new_value Original value = $value\n";
这是我的fribulate.pl 程序:
sub fribulate {
my $param = shift;
$value = $param * 2;
return $value * 6;
}
1;
当我运行我的程序时,我得到:
Fribulated Value = 54. Original Value = 9
等等?原来不是4.5吗? fribulate.pl 程序影响了我的 $value,因为它还使用了一个名为 $value 的变量。为了解决这个问题,Perl 创建了 package 命令来创建一个新的命名空间:
package Fribulate;
sub fribulate {
my $param = shift;
$value = $param * 2;
return $value * 3.1416;
}
1;
现在,fribulate.pl 程序不在 命名空间 main,而是在 命名空间 Fribulate。因此,fribulate.pl 中使用的$value 变量与我的$value 变量不同。
但是,如果我在其前面加上命名空间,我可以访问另一个命名空间中的变量:
require "fribulate.pl";
$value = 4.5;
$new_value = fribulate($value);
print "Fribulated Value = $new_value Original value = $value\n";
# Printing the value of $value from frimbulate.pl:
print "And in fribulate.pl, it's using $Fribulate::value\n";
如果您使用File:Find,您会看到这一点。要访问文件的全名,请使用$File::Find::name。要访问文件的目录,请使用$File::Find::dir。命名空间 File::Find 被添加到 File::Find 中的 $dir 和 $name 变量之前。
命名空间的问题是现在所有东西都在新的命名空间中,包括我在frimbulate.pl 中的frimbulate 函数。因此,我的原始程序还必须在函数前面添加命名空间才能工作:
require "fribulate.pl";
$value = 4.5;
$new_value = Frimbulate::fribulate($value);
print "Fribulated Value = $new_value Original value = $value\n";
为了解决这个问题,您在frimbulate.pl 程序中做了一些花哨的操作:
Package Frimbulate;
require Exporter;
@EXPORT = qw(frimbulate);
sub fribulate {
my $param = shift;
$value = $param * 2;
return $value * 3.1416;
}
1;
Exporter 包在@EXPORT 中的函数上撒上魔法粉1 并使它们可用于main 命名空间——所有东西所在的默认命名空间。因此,File::Copy 和 File::Basename 等模块使用 Exporter 允许您访问它们各自的 copy 和 basename 子例程,而无需在它们前面添加包名。
这现在被认为是不好的样式,因为您最终可能会在没有任何警告的情况下覆盖具有相同名称的其他子例程。在新的编写模块风格中,您不再自动导出 @EXPORT 数组中的所有函数。您会在 File::Path 中注意到这一点,它不会在没有我们明确请求的情况下自动将其函数导出到 main 命名空间中。相反,您将它们放在 @EXPORT_OK 中,这需要用户要求将它们推送到命名空间中:
Package Frimbulate;
require Exporter;
@EXPORT_OK = qw(frimbulate); #You have to request the frimbulate subroutine
sub fribulate {
my $param = shift;
$value = $param * 2;
return $value * 3.1416;
}
1;
# Now I have to ask that the frimbulate subroutine be import into my main namespace
require "fribulate.pl" qw(frimbulate);
$value = 4.5;
$new_value = fribulate($value);
print "Fribulated Value = $new_value Original value = $value\n";
现在应该为您提供足够的背景知识来阅读Perl Module 文档,并且很可能了解发生了什么。 Perlmod 文档涵盖了名称空间、符号表,甚至还有一些关于变量范围的内容。它包含很多信息,如果没有基本信息可能会有点吓人。
1 在使用 Exporter 模块时没有任何小精灵受到伤害。如果您查看使用perldoc -l Exporter 命令可以找到的Exporter.pm,您会发现它正在直接操作符号表。
【讨论】: