perl - 在 Perl 中生成 getter 和 setter 而不是硬编码它们有缺点吗?

标签 perl class module oop

在下面的示例模块中,getter 和 setter 是通过将匿名子例程添加到符号表来生成的。在以这种方式创建方法之后,生成的代码是否在功能上等同于具有手动编写的 getter 和 setter 的模块(在行为、速度等方面),或者这种方法是否具有某种固有的责任? (我已经做了一些基本的速度基准测试,到目前为止还没有发现任何差异。)

package Module;    
use strict;
use warnings;

BEGIN {
    my @attr = qw(author title number);
    no strict 'refs';
    for my $a (@attr){
        *{__PACKAGE__ . "::get_$a"} = sub { $_[0]->{$a}         };
        *{__PACKAGE__ . "::set_$a"} = sub { $_[0]->{$a} = $_[1] };
    }
}

sub new {
    my $class = shift;
    bless { @_ }, $class;
}

1;

最佳答案

如果两种情况下的结果代码相同,则运行时性能应该没有差异。但是,这通常是不可能的,除非您使用字符串 eval。创建您的子程序。例如,您提供的代码:

... = sub { $_[0]->{$a} };

将比您手动编写的代码慢一点:
sub foo { $_[0]->{'foo'} }

仅仅是因为前者必须先获取变量 $a 的值,然后才能将其用作散列的键,而后者则使用常量作为其散列键。另外,顺便说一句,shift通常往往比 $_[0] 更快.这是一些基准代码:
use Benchmark qw(cmpthese);

package Foo;

sub manual_shift { shift->{'foo'} }
sub manual_index { $_[0]->{'foo'} }

my $attr = 'foo';

*dynamic_shift = sub { shift->{$attr} };
*dynamic_index = sub { $_[0]->{$attr} };

package main;

my $o = bless { foo => 123 }, 'Foo';

cmpthese(-2, {
  manual_shift  => sub { my $a = $o->manual_shift },
  manual_index  => sub { my $a = $o->manual_index },
  dynamic_shift => sub { my $a = $o->dynamic_shift },
  dynamic_index => sub { my $a = $o->dynamic_index },
});

以及我系统上的结果:
                   Rate dynamic_index  manual_index dynamic_shift  manual_shift
dynamic_index 1799024/s            --           -3%           -4%           -7%
manual_index  1853616/s            3%            --           -1%           -4%
dynamic_shift 1873183/s            4%            1%            --           -3%
manual_shift  1937019/s            8%            4%            3%            --

它们非常接近,以至于差异可能会在噪音中消失,但经过多次试验,我认为您会发现“手动换档”变体是最快的。但是与所有像这样的微基准测试一样,你必须在你的硬件和你的 perl 版本上测试你的确切场景,以确保任何事情。

这里是字符串 eval 混合在一起。
eval "sub eval_index { \$_[0]->{'$attr'} }";
eval "sub eval_shift { shift->{'$attr'} }";

它应该与“手动”变体完全相同,加上或减去统计噪声。我的结果:
                   Rate dynamic_index manual_index dynamic_shift manual_shift eval_shift eval_index
dynamic_index 1820444/s            --          -1%           -2%          -3%        -4%        -5%
manual_index  1835005/s            1%           --           -1%          -2%        -3%        -4%
dynamic_shift 1858131/s            2%           1%            --          -1%        -2%        -3%
manual_shift  1876708/s            3%           2%            1%           --        -1%        -2%
eval_shift    1894132/s            4%           3%            2%           1%         --        -1%
eval_index    1914060/s            5%           4%            3%           2%         1%         --

同样,这些都非常接近,以至于您必须付出巨大的努力并进行许多试验才能从噪声中挑选出信号。但是使用常量作为散列键和使用变量(必须首先检索其值)作为散列键之间的区别应该显示出来。 (shift 优化是一个单独的问题,更有可能在 perl 的过去或 future 版本中以一种或另一种方式改变。)

关于perl - 在 Perl 中生成 getter 和 setter 而不是硬编码它们有缺点吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1390082/

相关文章:

c++ - 将模板类作为参数传递

java - 类对象和 JSP

Python-从类中的函数访问变量

javascript - JavaScript 中导入模块是如何工作的?

gradle - 更新 build.gradle 时 IntelliJ 模块设置发生变化

perl - Perl线程问题

perl - 如何使用 Perl 修改现有的 Excel 工作簿?

perl - plink-使用 Windows 中的 python 程序运行位于远程 Linux 服务器上的 perl 脚本时出现 @INC 错误中无法定位 .pl

perl - Perl 子程序中的变量不会释放内存

javascript - 更改显示模块模式之外的可配置设置对象