我正在阅读元编程并发现了这个练习:
http://ruby-metaprogramming.rubylearning.com/html/Exercise_1.html
问题是:
鉴于此类定义:
class A
def initialize
@a = 11
@@a = 22
a = 33
end
@a = 1
@@a = 2
a = 3
end
从类外部检索所有变量,输出如下:
1
2
3
11
22
33
现在,动态获取实例和类变量非常简单,甚至是构造函数中的局部变量(它是initialize方法的返回值)。但我很难理解如何获取局部变量a=3
。
据我所知,这是不可能的,因为首次读取类定义后局部变量不再存在。
我做这项工作的唯一迂回方式是将一个变量设置为声明类的“返回”值(缺少一个更好的术语),如下所示:
val = class A
a = 3
end
puts val # => 3
这是唯一的方法吗?
答案 0 :(得分:2)
问题似乎归结为:给出以下内容:
class A
attr_reader :instance_var
def initialize
@instance_var = (@instance_var ||= 0) + 1
instance_local_var = 33
puts "instance_local_variables = #{ local_variables }"
instance_local_var = 33
end
class_local_var = 3
puts "class_local_variables = #{ local_variables }"
class_local_var = 3
end
# class_local_variables = [:class_local_var]
#=> 3
可以确定instance_local_var
和class_local_var
的价值吗?
确定class_local_var
这个问题的答案显然是"没有"因为在class_local_var
执行 1 之后end
不再存在(已被标记为垃圾收集):
A.send(:local_variables)
#=> []
确定instance_local_var
a = A.new
# instance_local_variables = [:instance_local_var]
#=> #<A:0x007ff3ea8dbb80 @instance_var=1>
请注意@instance_var #=> 1
。
A.new
不会返回instance_local_var
的值,但因为该变量在initialize
的最后一行中被赋值,所以可以通过执行{{1}来获取该值再次。 2
initialize
但是有一个问题:
instance_local_var = a.send(:initialize)
#=> 33
第二次执行a.instance_var
#=> 2
会造成不必要的副作用。我对initialize
的定义是人为的,但它强调了第二次执行initialize
可能会产生许多不良副作用的事实。
现在让我们获取一个新实例。
initialize
再次,b = A.new
# instance_local_variables = [:instance_local_var]
#=> #<A:0x007fee0996e7c8 @instance_var=1>
。针对给定实例调用@instance_var=1
两次的副作用的一种可能的解决方法是子类initialize
并使用A
。
super
无法保证使用此方法可以避免不良副作用(例如,class B < A
attr_reader :b
def initialize
@b = super
end
end
B.new.b
#=> 33
a.instance_var
#=> 1
对于任何实例都可以执行仅应发生一次的数据库操作),但它似乎保留了初始实例{ {1}}未受影响。这当然都是假设的。
1。必须使用initialize
,因为a
2。 send
是必需的,因为A.private_methods.include?(:local_variables) #=> true
是私有的。
答案 1 :(得分:1)
你的问题不清楚。在标题中,您编写“局部变量”,但在示例中,您只提到实例变量和类变量。
对于实例变量,您可以使用Object#instance_variables来获取此时已知实例变量的列表。但请注意,实例变量是动态创建的,而不是在类声明时创建的。例如,给定类
class AA;
def initialize; @x=1; end;
def f; @y=1; end;
end
表达式
AA.new.instance_variables
将返回[:@x]
- :@y缺失,因为它尚不存在。
您没有办法自动(即不修改类)检索局部变量。正如 mudasobwa 在他的回答中解释的那样,你必须明确地传回一个约束力。
答案 2 :(得分:0)
可以通过从类定义返回绑定来获取对类的所有局部变量的访问权限(有关详细信息,请参阅Binding#local_variables
):
a = class A
v1 = 3.14
v2 = 42
binding
end
a.local_variable_get(:v1)
#⇒ 3.14
a.local_variable_get(:v2)
#⇒ 42
但主要问题是你为什么要这样做? local 变量旨在保留 local 。这就是它的表现方式。此外,一个人无法修改原始binding
中的局部变量(返回的是原始绑定的只读副本。)