如何在 Raku 中动态访问对象中的属性
How to access attributes in object dynamically in Raku
我想知道如何在 Raku
的运行时通过名称作为 Str
动态地 访问对象的属性。而不是:
#!/usr/bin/rakudo
class c0 {
has $!a0 = 1;
has $!a1 = 2;
method access(Str $m) {
if ($m eq "a0") { return $!a0; }
if ($m eq "a1") { return $!a1; }
}
method set(Str $m, $v) {
if ($m eq "a0") { $!a0 = $v; }
if ($m eq "a1") { $!a1 = $v; }
}
}
my $c = c0.new();
$c.set("a0", 3);
$c.set("a1", 4);
say $c.access("a0");
say $c.access("a1");
我想使用一些伪代码:
class c0 {
...
method access(Str $m) {
return self.$m;
}
method set(Str $m, $v) {
self.$m = $v;
}
}
这在 Raku
中可行吗?我需要使用哪种结构?
作为背景知识,我在考虑如何实现一个角色,为 class 添加关联功能,以透明地访问成员。属性名称将被参数化:如果我有一个 class class ports { has @!ports_; ... }
和一个实例 my $p = ports.new()
那么我希望能够使用下标语法通过 $p[.. .] .我试图弄清楚天气我可以定义 role acc [ Str $member] does Associative[Cool,Str] { ... }
然后通过 class ports does acc["ports_"] { ... }
定义端口
role acc
中的 AT-KEY
和 EXISTS-KEY
是使用动态属性访问实现的(如果可能的话)。
我不想使用“EVAL”。
这可以通过对属性进行一些内省来实现。但是,我想指出私有属性的确切意图是私有的。创建一个解决方法将它们作为 public 属性来处理是一种反模式,并引入了不必要的复杂性。
class c0 {
has $.a0 = 1;
has $.a1 = 2;
method access (Str $m) {
my $attribute = self.^attributes.first({ ~$_ eq '$!' ~ $m });
return unless $attribute;
$attribute.get_value(self); # 1
}
}
my $c = c0.new;
say $c.access('a0');
要设置值,可以在属性上使用 .set_value
方法。
method set (Str $m, $v) {
...
$attribute.set_value(self, $v);
}
出于历史目的,旧答案留在此处。
是的,在 Raku 中这样的事情是可能的。您甚至不需要显式定义 access
方法。
class c0 {
has $.a0 = 1;
has $a.1 = 2;
}
my $c = $c0.new;
say $c.'a0'(); # 1
之所以有效,是因为 Raku 为 类 的 public 变量创建了一个访问器方法,当您使用 .'a0'()
时会调用该方法。使用带引号的方法名称需要 ()
。
您更改了 post 以添加有关如何执行此操作的问题:
role acc [ Str $member] does Associative[Cool,Str] { ... }
class ports does acc["ports_"] { has @!ports_; ... }
答案当然是,不要那样做。
我是说你可以,但你真的不应该。
我是说你真的不应该。
您还表示要使用 []
进行索引。
问题是 Positional
而不是 Associative
.
(我忽略了在属性名称末尾添加_
没有意义的事实。通常在Perl或Python中添加_
表示private
,但我们不需要在 Raku 中这样做。)
正确的做法是将数组放入角色中。
role Array::Access [::OF = Cool] does Positional[OF] {
has OF @!array-access handles < AT-POS >;
}
class Ports does Array::Access {
# allows you to access it as self!ports inside of this class
method !ports () is raw { @!array-access }
}
这表明添加一个角色来执行此操作可能有点矫枉过正。
class Ports does Positional[Cool] {
has Cool @!ports handles < AT-POS >;
}
如果您真的非常想按照您要求的方式进行操作,则以下方法有效。
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
# a way to quickly access the attribute
# (hopefully no-one tries to add an attribute of this name to their class)
has $!inner-array handles < AT-POS >;
# set $!inner-array
submethod TWEAK (){
$!inner-array := self.^attributes.first(name).get_value(self);
}
}
class Ports does Inner::Array::Access['@!ports'] {
has @!ports;
# a quick way to add a way to set @!ports for our test
submethod BUILD( :@!ports ){}
}
my Ports $v = ports => [0,10,20,30];
say $v[2]; # 20
可能您的想法是将 self.^attributes
嵌入 AT-POS
。
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
method AT-POS ( \index ) is raw {
self.^attributes.first(name).get_value(self).AT-POS(index);
}
}
那会很慢,因为每次访问单个元素时它都必须进行所有这些查找。
我想知道如何在 Raku
的运行时通过名称作为 Str
动态地 访问对象的属性。而不是:
#!/usr/bin/rakudo
class c0 {
has $!a0 = 1;
has $!a1 = 2;
method access(Str $m) {
if ($m eq "a0") { return $!a0; }
if ($m eq "a1") { return $!a1; }
}
method set(Str $m, $v) {
if ($m eq "a0") { $!a0 = $v; }
if ($m eq "a1") { $!a1 = $v; }
}
}
my $c = c0.new();
$c.set("a0", 3);
$c.set("a1", 4);
say $c.access("a0");
say $c.access("a1");
我想使用一些伪代码:
class c0 {
...
method access(Str $m) {
return self.$m;
}
method set(Str $m, $v) {
self.$m = $v;
}
}
这在 Raku
中可行吗?我需要使用哪种结构?
作为背景知识,我在考虑如何实现一个角色,为 class 添加关联功能,以透明地访问成员。属性名称将被参数化:如果我有一个 class class ports { has @!ports_; ... }
和一个实例 my $p = ports.new()
那么我希望能够使用下标语法通过 $p[.. .] .我试图弄清楚天气我可以定义 role acc [ Str $member] does Associative[Cool,Str] { ... }
然后通过 class ports does acc["ports_"] { ... }
定义端口
role acc
中的 AT-KEY
和 EXISTS-KEY
是使用动态属性访问实现的(如果可能的话)。
我不想使用“EVAL”。
这可以通过对属性进行一些内省来实现。但是,我想指出私有属性的确切意图是私有的。创建一个解决方法将它们作为 public 属性来处理是一种反模式,并引入了不必要的复杂性。
class c0 {
has $.a0 = 1;
has $.a1 = 2;
method access (Str $m) {
my $attribute = self.^attributes.first({ ~$_ eq '$!' ~ $m });
return unless $attribute;
$attribute.get_value(self); # 1
}
}
my $c = c0.new;
say $c.access('a0');
要设置值,可以在属性上使用 .set_value
方法。
method set (Str $m, $v) {
...
$attribute.set_value(self, $v);
}
出于历史目的,旧答案留在此处。
是的,在 Raku 中这样的事情是可能的。您甚至不需要显式定义 access
方法。
class c0 {
has $.a0 = 1;
has $a.1 = 2;
}
my $c = $c0.new;
say $c.'a0'(); # 1
之所以有效,是因为 Raku 为 类 的 public 变量创建了一个访问器方法,当您使用 .'a0'()
时会调用该方法。使用带引号的方法名称需要 ()
。
您更改了 post 以添加有关如何执行此操作的问题:
role acc [ Str $member] does Associative[Cool,Str] { ... }
class ports does acc["ports_"] { has @!ports_; ... }
答案当然是,不要那样做。
我是说你可以,但你真的不应该。
我是说你真的不应该。
您还表示要使用 []
进行索引。
问题是 Positional
而不是 Associative
.
(我忽略了在属性名称末尾添加_
没有意义的事实。通常在Perl或Python中添加_
表示private
,但我们不需要在 Raku 中这样做。)
正确的做法是将数组放入角色中。
role Array::Access [::OF = Cool] does Positional[OF] {
has OF @!array-access handles < AT-POS >;
}
class Ports does Array::Access {
# allows you to access it as self!ports inside of this class
method !ports () is raw { @!array-access }
}
这表明添加一个角色来执行此操作可能有点矫枉过正。
class Ports does Positional[Cool] {
has Cool @!ports handles < AT-POS >;
}
如果您真的非常想按照您要求的方式进行操作,则以下方法有效。
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
# a way to quickly access the attribute
# (hopefully no-one tries to add an attribute of this name to their class)
has $!inner-array handles < AT-POS >;
# set $!inner-array
submethod TWEAK (){
$!inner-array := self.^attributes.first(name).get_value(self);
}
}
class Ports does Inner::Array::Access['@!ports'] {
has @!ports;
# a quick way to add a way to set @!ports for our test
submethod BUILD( :@!ports ){}
}
my Ports $v = ports => [0,10,20,30];
say $v[2]; # 20
可能您的想法是将 self.^attributes
嵌入 AT-POS
。
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
method AT-POS ( \index ) is raw {
self.^attributes.first(name).get_value(self).AT-POS(index);
}
}
那会很慢,因为每次访问单个元素时它都必须进行所有这些查找。