perl - 在 Perl 严格模式下从变量访问完全限定的变量名
问题描述
我有一系列为我的脚本执行输出功能的模块。有时直接调用模块——它被调用View
——有时使用扩展它的子类来代替(View::ChildName
)。视图our $THEMENAME = 'default';
在加载时声明,但子在加载时声明自己的特定$THEMENAME
。
问题:在new()
子主题上调用时,它调用my $self = $class->next::method(%params);
(使用mro
)在扩展父类之前获取父类设置的一些核心内容。核心位之一是父类设置$self->{'themeName'}
. 但是,如果它只是调用$THEMENAME
,它会获取父级的设置:“默认”。
strict
我可靠并成功解决此问题的唯一方法是暂时关闭并执行以下操作:
my $packName = ref $self;
{
no strict;
$self->{'themeName'} = ${${packName} . "::THEMENAME"};
}
这可行,但在分析代码时,如果频繁创建对象,这实际上增加了比我预期的更多的开销。我尝试了始终使用父包名的替代方法,例如子集$View::THEMENAME
。这有效,但前提是主题名称是在模块内new
而不是在模块加载时设置的;如果它正在加载,如果在脚本过程中创建了几个不同的子对象(不同的子对象),则会出现不稳定的行为。
这些选项似乎都不太理想。有没有好的方法来做到这一点?我发现的唯一一件事是这个老问题,我认为合并Moo
可能会增加比我试图通过摆脱当前no strict
块来避免的开销更多的开销。有没有添加到更现代的 Perl 版本中来解决我的问题?
另一种方法是一起躲避这个问题,并$self->{'themeName'}
在每个子对象的new
方法中简单地设置,尽管我试图避免这种变化,因为预计$THEMENAME
会存在相当多的遗留子类。
View.pm 的最小可重现示例:
use strict;
package View;
our $THEMENAME = 'default';
sub new {
my $class = shift;
my $params = shift;
my $self = { 'setting' => $params{'setting'} };
bless $self, $class;
$self->{'themeName'} = $THEMENAME;
return $self;
}
和View/Child.pm
:
use strict;
use mro;
package View::Child;
use parent 'View';
our $THEMENAME = 'child';
sub new {
my $class = shift;
my $params = shift;
my $self = $class->next::method($params);
bless $self, $class;
say STDOUT $self->{'themeName'};
# Prints 'default' not 'child'.
return $self;
}
现在调用它的脚本:
use View::Child;
my $object = View::Child->new();
如果您将第一个代码块添加到View.pm
,它会给出预期的结果,但似乎每次调用增加了大约 9 毫秒new
- 比它处理我在更长的全长new
方法中拥有的所有其他东西所花费的时间更多-- 如果程序运行多次迭代,它会加起来:
use strict;
package View;
our $THEMENAME = 'default';
sub new {
my $class = shift;
my $params = shift;
my $self = { };
bless $self, $class;
my $packName = ref $self;
{
no strict;
$self->{'themeName'} = ${${packName} . "::THEMENAME"};
}
return $self;
}
解决方案
类属性的概念是你应该忘记的(在 Perl 中)。模块可以有常量和可能的变量,但它们不应该被视为类的一部分。
我看到您可以采取四种方法:
- 更新子构造函数中的属性。
- 提供值作为参数
- 覆盖访问器
- 提供默认值作为方法
更新子构造函数中的属性
# View.pm
sub new {
my ($class, $params) = @_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = 'default';
return $self;
}
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
sub new {
my ($class, $params) = @_;
my $self = $class->next::method($params);
$self->{ themeName } = 'child';
return $self;
}
提供值作为参数
# View.pm
sub new {
my ($class, $params) = @_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = $params->{ themeName } // 'default';
return $self;
}
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
sub new {
my ($class, $params) = @_;
my $self = $class->next::method({ themeName => 'child', %$params });
return $self;
}
覆盖访问器
# View.pm
sub new {
my ($class, $params) = @_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
return $self;
}
sub themeName { 'default' }
# Child/View.pm
# No need to override `new`.
sub themeName { 'child' }
提供默认值作为方法
# View.pm
sub new {
my ($class, $params) = @_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = $class->defaultThemeName;
return $self;
}
sub defaultThemeName { 'default' }
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
# No need to override `new`.
sub defaultThemeName { 'child' }
推荐阅读
- windows - 如何在 gitlab-ci 共享运行器上使用自定义 windows docker 容器
- json - `null` 和 `no output` 之间的区别
- python - 如何复制此尝试/除了代码中的所有其他输入?
- java - 无法在项目 myProject 上执行目标 org.eclipse.jetty:jetty-maven-plugin:9.4.9:run (default-cli):失败
- c++ - dfs 实现的各种变体
- android - 开始使用 Android NFC
- javascript - 存储 cookie 时忽略路径参数
- bash - 从 bash 中的文件获取模块和版本
- flutter - 键盘在 TextField 点击时不断关闭
- java - Java 模式 \p{InBasicLatin} 允许使用 ASCII 以外的字符,例如韩语和泰米尔语