Perl:如何动态创建对象?

时间:2010-08-04 13:46:21

标签: perl oop object packages

我的目标是能够像这样使用$obj

print $obj->hello() . $obj->{foo};

我想创建一个内联对象,也许使用像这样的东西:

my $obj = (
    foo => 1,
    hello => sub { return 'world' }
);

但是当我尝试使用$obj作为对象时,我收到一条错误消息,指出 $ obj尚未被祝福。是否有一些基类(如PHP中的stdClass)我可以用来祝福哈希,以便我可以将它用作对象?


对于那些了解JavaScript的人,我尝试执行以下操作,但在Perl中:

# JS CODE BELOW
var obj = { foo: 1, hello: function () { return 'world' } };
echo obj.hello() + obj.foo;

8 个答案:

答案 0 :(得分:14)

Perl需要一点帮助才能做到这一点。因为它不将存储在哈希中的代码引用视为“方法”。方法被实现为包符号表中的条目。 Perl比JavaScript更像面向类,它自豪地宣称它更像面向对象(在个人对象上)。

为了实现这一功能,您必须创建一个以这种方式映射引用的类。绕过符号表中方法的方法是AUTOLOAD方法。如果包中包含AUTOLOAD子例程,当调用Perl在继承链中找不到的祝福对象时,它将调用AUTOLOAD并设置包范围(our )变量$AUTOLOAD将包含函数的完整名称。

通过获取完全限定子名称的最后一个节点(在最后一个'::'之后),我们得到被调用方法的名称。我们查看该位置是否有coderef,如果有,我们可以返回它。

package AutoObject;

use strict;
use warnings;
use Carp;
use Params::Util qw<_CODE>;
our $AUTOLOAD;

sub AUTOLOAD {
    my $method_name = substr( $AUTOLOAD, index( $AUTOLOAD, '::' ) + 2 );
    my ( $self )    = @_;
    my $meth        = _CODE( $self->{$method_name} );
    unless ( $meth ) { 
        Carp::croak( "object does not support method='$method_name'!" );
    }
    goto &$meth;
}


1;

然后你会把对象加入到那个班级中:

package main;

my $obj 
    = bless { foo => 1
      , hello => sub { return 'world' }
      }, 'AutoObject';

print $obj->hello();

通常,在AUTOLOAD子I“水泥”行为中。也就是说,我在包符号表中创建条目以避免下次AUTOLOAD。但这通常适用于合理定义的类行为。

我还设计了一个QuickClass,它为每个声明的对象创建一个包,但是它包含了很多符号表争论,现在使用Class::MOP可能会更好。


根据Eric Strom的建议,您可以将以下代码添加到AutoObject包中。任何人import - d use(参数AutoObject)都会随时调用'object'子。

# Definition:
sub object ($) { return bless $_[0], __PACKAGE__; };

sub import { # gets called when Perl reads 'use AutoObject;'
    shift; # my name
    return unless $_[0] eq 'object'; # object is it's only export
    use Symbol;
    *{ Symbol::qualify_to_reference( 'object', scalar caller()) }
        = \&object
        ;
}

然后,当你想创建一个“对象文字”时,你可以这样做:

use AutoObject qw<object>;

表达式为:

object { foo => 1, hello => sub { return 'world' } };

你甚至可以这样做:

object { name  => 'World'
       , hello => sub { return "Hello, $_[0]->{name}"; } 
       }->hello()
       ;

你有一个“对象文字”表达式。也许这个模块可以更好地称为Object::Literal

答案 1 :(得分:4)

从CPAN尝试Hash::AsObject

答案 2 :(得分:4)

更多Perlish方法是为对象的所需方法创建单独的命名空间,并为对象bless创建可用于对象的方法。执行此操作的代码仍然非常简洁。

my $obj = bless { foo => 1 }, "bar";
sub bar::hello { return 'world' };

正如gbacon建议的那样,如果你愿意写$obj->{hello}->()代替$obj->hello(),你可以跳过祝福行动。

my $obj = { foo => 1, hello => sub { return 'world' } };

答案 3 :(得分:2)

$obj将是一个标量,所以无论你赋予它什么,都必须是一个标量。你可以说

my %obj = ( foo => 1, hello => sub { return 'world' });

my $obj = { foo => 1, hello => sub { return 'world' }};

后者使用花括号创建一个哈希引用(它是一个标量,因此它可以进入$obj)。但是,要获取哈希引用中的内容,必须使用箭头运算符。类似于$obj->{foo}&{$obj->{hello}}

除非您需要列出哈希值或类似内容,否则通常最好使用第一种方法。

无论哪种方式,您都无法说$obj->hello()。 Perl将这种语法用于其自己的OOP风格,它将hello函数放在一个单独的包中,您的引用已被bless编入。像:

package example;
sub new {} { my $result = {}; return bless $result, 'example' }
sub hello { return 'world' }

package main;
my $obj = example->new();

正如您所看到的,您可以调用的方法已经定义,添加更多内容并非易事。有一些神奇的方法可以用来做这样的事情,但实际上,这是不值得的。 &{$obj{hello}}(或&{$obj->{hello}}作为参考)比尝试让Perl像Javascript一样工作的努力要少。

答案 4 :(得分:2)

Perl的拼写有点不同:

my $obj = { foo => 1, hello => sub { return "world" } };
print $obj->{hello}() . $obj->{foo};

但代码很尴尬。您看到的有关未被祝福的引用的警告告诉您,您的对象未在the way Perl expects中实现。 bless运算符使用包标记一个对象,在该对象中开始搜索其方法。

告诉我们您在问题域方面要做什么,我们可以在Perl中提供更自然的方法建议。

答案 5 :(得分:1)

在您创建对象的任何函数中,您需要在对象上调用bless以启用方法调用。

例如:

package MyClass;

sub new
{
  my $obj = {
    foo => 1
  };

  return bless($obj, "MyClass");
}

sub hello
{
  my $self = shift;
  # Do stuff, including shifting off other arguments if needed
}

package main;
my $obj = MyClass::new();

print "Foo: " . $obj->{foo} . "\n";
$obj->hello();

编辑:如果您希望能够使用子例程引用为对象提供动态功能......

首先,您可以像这样创建代码引用(在此哈希构造函数示例中):

my $obj = {
  foo => 1,
  hello => sub { print "Hello\n"; },
}

然后你可以像这样调用它:

my $obj = MyClass::new(); # or whatever
$obj->{hello}->(@myArguments);

有点麻烦,但它确实有效。 (你甚至可能不需要第二支箭,但我不确定。)

答案 6 :(得分:0)

Perl中的方法不是像Python中那样的对象属性。方法是与对象关联的中的普通常规函数函数。常规函数为自引用提供了额外的参数。

您不能将动态创建的函数作为方法。

以下是perldoc perlobj的引用:

   1.  An object is simply a reference that happens to know which class it
       belongs to.

   2.  A class is simply a package that happens to provide methods to deal
       with object references.

   3.  A method is simply a subroutine that expects an object reference
       (or a package name, for class methods) as the first argument.

哦,祝福()是如何建立引用和包之间的连接。

答案 7 :(得分:0)

我建议使用Class :: Struct,如perltoot手册页中所述。

除了解释文档之外,让我引用它,因为它解释了它是如何工作的:

&#34;它的作用是为您提供一种方式来宣告&#34;具有字段属于特定类型的对象的类。执行此操作的函数,不足为奇地调用struct()。因为结构或记录不是Perl中的基类型,所以每次要创建一个类来提供类似记录的数据对象时,您自己必须定义一个new()方法,并为每个记录定义单独的数据访问方法。 #39;的字段。你很快就会对这个过程感到厌倦。 Class :: Struct :: struct()函数缓解了这种乏味。&#34;

仍然引用文档是如何实现它的一个示例方法:

use Class::Struct qw(struct);
use Jobbie;  # user-defined; see below
struct 'Fred' => {
    one        => '$',
    many       => '@',
    profession => 'Jobbie',  # does not call Jobbie->new()
};
$ob = Fred->new(profession => Jobbie->new());
$ob->one("hmmmm");
$ob->many(0, "here");
$ob->many(1, "you");
$ob->many(2, "go");
print "Just set: ", $ob->many(2), "\n";
$ob->profession->salary(10_000);