Объявление переменных экземпляра, повторяющих хэш!

я хочу сделать следующее:

Я хочу объявить переменные экземпляра класса, перебирающего словарь.

Предположим, что у меня есть этот хэш

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

и я хочу, чтобы каждый ключ был переменной экземпляра класса. Я хочу знать, могу ли я объявить переменные, повторяющие этот хеш. Что-то вроде этого:

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

Я знаю, что это не работает! Я поместил этот фрагмент кода только для того, чтобы увидеть, сможете ли вы лучше понять, что я хочу.

Спасибо!


person flyer88    schedule 23.10.2009    source источник


Ответы (4)


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

Собственный класс — это специальный класс, принадлежащий только одному объекту, поэтому методы, определенные там, будут методами экземпляра этого объекта, но не будут принадлежать другим экземплярам обычного класса объекта.

person Chuck    schedule 23.10.2009

Ответ Чака лучше, чем мои последние две попытки. Собственный класс не self.class, как я думал; чтобы понять это, потребовался лучший тест, чем я написал.

Используя мой старый код, я протестировал его следующим образом и обнаружил, что действительно манипулировали классом, а не экземпляром:

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}" }

Результат был:

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

Это явно опровергает мое предположение. Мои извинения, Чак, ты был прав с самого начала.

Старый ответ:

attr_accessor работает только при оценке в определении класса, а не при инициализации экземпляра. Поэтому единственный способ напрямую сделать то, что вы хотите, — это использовать instance_eval со строкой:

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

Чтобы проверить это, попробуйте:

c = MyClass.new :my_var => 1
puts c.my_var
person Robert K    schedule 23.10.2009
comment
self.class не является собственным классом. Изменение self.class повлияет на каждый объект, принадлежащий классу, в то время как изменение собственного класса объекта повлияет только на этот объект. В некоторых случаях они взаимозаменяемы, но не в целом. - person Chuck; 25.10.2009
comment
Что касается версии 1.8.7, я проверил это и обнаружил, что ваше утверждение неверно. Тем не менее, это единственная версия, которую я тестировал. - person Robert K; 26.10.2009
comment
Тогда ваш тест был ошибочным. Запустите это: "Hello".class.class_eval {def size() 9000 end}; puts "Bye".size и вы увидите, что переопределение метода в Hello.class также повлияло на поведение совершенно не связанной строки Bye. Это справедливо для любой версии Ruby, с которой вы, вероятно, столкнетесь. - person Chuck; 27.10.2009
comment
Прошу прощения, Чак, лучшие тесты показывают, что ты прав. Но это, кажется, не имеет никакого смысла, поэтому мне придется задать вопрос об этом. - person Robert K; 27.10.2009

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

OpensStructable — это модуль миксина, который может обеспечить поведение OpenStruct для любого класса или объекта. OpenStructable позволяет расширять объекты данных с произвольными атрибутами.

person ykaganovich    schedule 23.10.2009

person    schedule
comment
спасибо за это необязательное замечание! но attr_accessor в хэш-итераторе BLOCK у меня не работает... Как я могу это сделать? (создайте итерацию attr_accessors) - person flyer88; 23.10.2009
comment
Чтобы выполнить attr_accessor, вам нужно находиться в контексте класса. Способ получить контекст класса для экземпляра — использовать собственный класс объекта, как в моем ответе. - person Chuck; 24.10.2009