Ruby:从类中检索局部变量

时间:2017-04-19 05:57:48

标签: ruby

我正在阅读元编程并发现了这个练习:

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

这是唯一的方法吗?

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_varclass_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中的局部变量(返回的是原始绑定的只读副本。)