【问题标题】:How to get attributes that were defined through attr_reader or attr_accessor如何获取通过 attr_reader 或 attr_accessor 定义的属性
【发布时间】:2012-04-17 21:52:22
【问题描述】:

假设我有一个班级A

class A
  attr_accessor :x, :y

  def initialize(x,y)
    @x, @y = x, y
  end
end

如何在不知道它们的确切命名方式的情况下获得 xy 属性。

例如

a = A.new(5,10)
a.attributes # => [5, 10]

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    使用内省,卢克!

    class A
      attr_accessor :x, :y
    
      def initialize(*args)
        @x, @y = args
      end
    
      def attrs
        instance_variables.map{|ivar| instance_variable_get ivar}
      end
    end
    
    a = A.new(5,10)
    a.x # => 5
    a.y # => 10
    a.attrs # => [5, 10]
    

    【讨论】:

    • 注意:attrs 将返回 all 实例变量,而不仅仅是attr_accessor
    • @Jonah:是的,那是当时的假设。更精确的方法可以参考this answer
    【解决方案2】:

    虽然 Sergio 的回答有帮助,但它会返回所有实例变量,如果我正确理解 OP 的问题,这不是所要求的。

    如果您只想返回具有例如的“属性”一个mutator,你必须做一些稍微复杂的事情,比如:

    attrs = Hash.new
    instance_variables.each do |var|
      str = var.to_s.gsub /^@/, ''
      if respond_to? "#{str}="
        attrs[str.to_sym] = instance_variable_get var
      end
    end
    attrs
    

    这仅返回使用 attr_accessor(或手动创建的 mutator)声明的属性,并隐藏内部实例变量。如果你想要那些用 attr_reader 声明的,你可以做类似的事情。

    【讨论】:

    • 考虑到这个问题,我认为这是一个更好的答案。我会附加到它上面。每当将符号添加为访问器或写入器时,Ruby 都会创建 {Variable}= 方法。回应?只是确保存在具有 {Variable}= 名称的有效方法。第一部分是去掉返回变量的第一个“@”。在 OP 示例中,将创建方法“x=”和“y=”,您正在使用 @x;x= 和 @y;y= 查找它们
    • 在某些情况下,这将包括:attributes=。 ://
    【解决方案3】:
    class A
      ATTRIBUTES = [:x, :y]
      attr_accessor *ATTRIBUTES
    
      def initialize(x,y)
        @x, @y = x, y
      end
    
      def attributes
        ATTRIBUTES.map{|attribute| self.send(attribute) }
      end
    end
    

    这可能不是 DRY-est,但如果您只关心为一个类(而不是所有东西都继承自的基类)执行此操作,那么这应该可行。

    【讨论】:

    • 最佳答案!
    【解决方案4】:

    见其他Stack Overflow Question。它们覆盖attr_accessor

      def self.attr_accessor(*vars)
        @attributes ||= []
        @attributes.concat vars
        super(*vars)
      end
    
      def self.attributes
        @attributes
      end
    
      def attributes
        self.class.attributes
      end
    

    【讨论】:

      【解决方案5】:

      如果您在属性上定义了attr_writers/attr_accessors,则可以通过匹配=$ 正则表达式轻松检索它们:

      A.instance_methods.each_with_object([]) { |key, acc| acc << key.to_s.gsub(/=$/, '') if key.match(/\w=$/) }
      

      A.instance_methods.each_with_object([]) { |key, acc| acc << key if key = key.to_s.match(/^(.*\w)=$/)&.[](1) }
      

      【讨论】:

        【解决方案6】:

        当你使用 attr_accessor 定义类中的属性时,Ruby 使用 refexion 定义了几个方法,对于声明的每个属性,一个获取值,另一个设置,一个与属性同名的实例变量

        你可以看到这个方法使用

        p A.instance_methods
        
        [:x, :x=, :y, :y=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?,..
        

        所以这个属性是可访问的,在类之外,有

        p "#{a.x},#{a.y}"
        

        或者类内部通过对应的实例变量

        class A
          ...
          def attributes
            [@x,@y]
          end
          ...
        end
        p a.attributes   #=> [5,10]
        

        【讨论】:

        • 感谢您的回复,但我询问了一个我不知道属性名称的情况,因为否则它不会是 DRY 方法。例如,如果我决定添加额外的属性@d@e@f。然后我必须手动将它们添加到attributes方法以及initialize中,这会导致代码重复。
        • 在这种情况下,使用 Sergio 的解决方案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多