Declaring instance variables iterating over a hash

2019-03-11 03:30发布

问题:

i want to do the following:

I want to declare the instance variables of a class iterating over a dictionary.

Let's assume that i have this hash

hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}

and i want to have each key as instance variable of a class. I want to know if i could declare the variables iterating over that hash. Something like this:

class MyClass
  def initialize()
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    hash.each do |k,v|
      @k = v
    end
  end
end

I know this doesn't work! I only put this piece of code to see if you could understand what i want more clearly.

Thanks!

回答1:

class MyClass
  def initialize()
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    hash.each do |k,v|
      instance_variable_set("@#{k}",v)
      # if you want accessors:
      eigenclass = class<<self; self; end
      eigenclass.class_eval do
        attr_accessor k
      end
    end
  end
end

The eigenclass is a special class belonging just to a single object, so methods defined there will be instance methods of that object but not belong to other instances of the object's normal class.



回答2:

class MyClass
  def initialize
    # define a hash and then
    hash.each do |k,v|
      # attr_accessor k # optional
      instance_variable_set(:"@#{k}", v)
    end
  end
end


回答3:

Chuck's answer is better than my last two attempts. The eigenclass is not self.class like I had thought; it took a better test than I had written to realize this.

Using my old code, I tested in the following manner and found that the class was indeed manipulated and not the instance:

a = MyClass.new :my_attr => 3
b = MyClass.new :my_other_attr => 4

puts "Common methods between a & b:"
c = (a.public_methods | b.public_methods).select { |v| a.respond_to?(v) && b.respond_to?(v) && !Object.respond_to?(v) }
c.each { |v| puts "    #{v}" }

The output was:

Common methods between a & b:
    my_other_attr=
    my_attr
    my_attr=
    my_other_attr

This clearly disproves my presupposition. My apologies Chuck, you were right all along.

Older answer:

attr_accessor only works when evaluated in a class definition, not the initialization of an instance. Therefore, the only method to directly do what you want is to use instance_eval with a string:

class MyClass
  def initialize(params)
    #hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    params.each do |k,v|
      instance_variable_set("@#{k}", v)
      instance_eval %{
        def #{k}
          instance_variable_get("@#{k}")
        end
        def #{k}= (new_val)
          instance_variable_set("@#{k}", new_val)
        end
      }
    end
  end
end

To test this try:

c = MyClass.new :my_var => 1
puts c.my_var


回答4:

http://facets.rubyforge.org/apidoc/api/more/classes/OpenStructable.html

OpensStructable is a mixin module which can provide OpenStruct behavior to any class or object. OpenStructable allows extention of data objects with arbitrary attributes.