如何为动态实例变量设置 attr_accessor?
Posted
技术标签:
【中文标题】如何为动态实例变量设置 attr_accessor?【英文标题】:How do I set an attr_accessor for a dynamic instance variable? 【发布时间】:2011-06-25 06:02:01 【问题描述】:我在我的类中动态创建了一个实例变量:
class Mine
attr_accessor :some_var
def intialize
@some_var = true
end
def my_number num
self.instance_variable_set "@my_#num", num
end
end
我现在如何将@my_#num
设为 attr 值?
例如我希望能够做到这一点:
dude = Mine.new
dude.my_number 1
dude.my_1
=> 1
【问题讨论】:
【参考方案1】:另一个解决方案,define_singleton_method
:
class Mine
def my_number num
define_singleton_method("num_#num") num
end
end
所有这些解决方案的一个副作用是,如果你用不同的数字多次调用它,你最终会在你的对象上得到一堆方法:
dude = Mine.new
dude.my_number 1
dude.my_number 5
dude.my_1
=> 1
dude.my_5
=> 5
我们可以通过删除旧方法来解决这个问题:
class Mine
def my_number num
old_num = @num
if @num
# need to use `old_num` local variable
# instance var scope is different inside `class_eval`
singleton_class.class_eval remove_method("num_#old_num")
end
@num = num
define_singleton_method("num_#num") @num
end
end
【讨论】:
【参考方案2】:这个答案不会污染类空间,例如..如果我这样做mine.my_number 4
,那么Mine
的其他实例将无法获得my_4
方法.. 发生这种情况是因为我们使用了单例类对象而不是类。
class Mine
def my_number num
singleton_class.class_eval attr_accessor "my_#num"
send("my_#num=", num)
end
end
a = Mine.new
b = Mine.new
a.my_number 10 #=> 10
a.my_10 #=> 10
b.my_10 #=> NoMethodError
【讨论】:
使用singleton_class
有什么原因吗?同样是单线,class_eval attr_accessor :"my_#num"
语法会更干净:)
@HalilÖzgür 您只想为该实例定义该方法,这就是您使用singleton_class
的原因,如果您使用self.class
,那么所有实例都会获得该方法而您不希望那样.你的语法是对的,我会做出改变
btw singleton_class 和 class << self
是一样的
我的意思是上面的代码在没有singleton_class
的情况下产生相同的输出。
@HalilÖzgür class_eval
是类方法,而不是实例方法【参考方案3】:
较旧的线程,但我发现它很有用,谢谢。这是代码 Dorkus Prime 的答案,但也从哈希中的名称\值中获取实例变量
@cookies = browser.cookies.to_a
@cookies.each do |cookie|
self.class.__send__(:attr_accessor, "#cookie[:name]")
self.__send__("#cookie[:name]=",cookie[:value])
end
【讨论】:
【参考方案4】:您可能想要使用 OpenStruct:
require "ostruct"
class Mine < OpenStruct
end
dude = Mine.new
dude.my_number = 1
dude.my_number # => 1
我不知道你为什么要 dude.my_1
返回 1 - 这不是把你已经拥有的东西还给你吗?
【讨论】:
我很好奇你如何继承 OpenStruct 和 ActiveRecord::Base @Trip 你不能从多个类继承。【参考方案5】:这里的两种方法存在一个问题...如果在一个实例中设置了一个实例变量,那么它的访问器将对所有实例都可用,因为您是在 self.class
而不是 self 上定义方法。
dude = Mine.new
dude.my_number 1
puts dude.my_1
dudette = Mine.new
dudette.my_1 = 2 # works, but probably shouldn't
dudette.my_number 2
dude.my_2 = 3 # works, but probably shouldn't
您可能想要做的只是修改具有实例变量的实例:
class Mine
# ...
def my_number num
class << self
attr_accessor "my_#num"
end
self.send("my_#num=", num)
end
end
这样,实例变量只能获得创建它们的对象的访问器。我也没有打扰 instance_variable_set,因为如果您正在设置访问器,那么我认为重用它会更好。但这是一种风格的召唤。这里最重要的是调用class << self
而不是self.class
。
【讨论】:
当我尝试这个时,我收到错误“未定义的局部变量或方法 `num' for #简单。您可以在 my_number 方法中动态定义属性读取器:
def my_number num
self.instance_variable_set "@my_#num", num
self.class.class_eval do
define_method("my_#num") num
end
end
看看是否适合你
【讨论】:
【参考方案7】:这可以使用__send__
来完成。这里:
class Mine
attr_accessor :some_var
def intialize
@some_var = true
end
def my_number num
self.class.__send__(:attr_accessor, "my_#num")
self.__send__("my_#num=", num)
end
end
dude = Mine.new
dude.my_number 1
puts dude.my_1
=> 1
【讨论】:
这将在所有实例上定义访问器,而不仅仅是您在其上调用my_number
的那个。我添加了一个额外的答案,它只为您添加实例变量的实例添加方法。以上是关于如何为动态实例变量设置 attr_accessor?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 dev vs prod @ionic/app-scripts 设置动态环境变量?