我的目标是能够像这样使用$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;
答案 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);