【问题标题】:override to_s while keeping output of inspect在保持检查输出的同时覆盖 to_s
【发布时间】:2012-03-01 21:51:27
【问题描述】:

当我使用 puts 时,我重写了 to_s 方法以获得漂亮的输出,但同时我似乎失去了检查对象的能力。有没有办法在覆盖 to_s 时获得检查的正常输出?

class Person
  attr_accessor :first, :last, :birthdate
  def initialize(first=nil, last=nil, birthdate=nil)
    @first, @last, @birthdate = first, last, birthdate
  end
  def age
    if birthdate
      Time.now.year-birthdate
    else
      0
    end
  end
  def to_s
    "#{@first} #{@last} (#{age})"
  end
end

me = Person.new("Peter", "Marien", 1962)
p me  >>Peter Marien (50)
p me.inspect  >>"Peter Marien (50)"

#Need #<Person:0x1ec2550 @first="Peter", @last="Marien", @birthdate=1962>

【问题讨论】:

  • 这个错误报告讨论了同样的问题:bugs.ruby-lang.org/issues/4453 但是我没有看到票证有任何进展。
  • 所以这是一个错误,没有意识到..,感谢链接

标签: ruby oop overriding


【解决方案1】:

Ruby 文档 clearly states,默认情况下 inspect 使用 to_s 作为其输出。

如果您不需要对象的地址,您可以提供自己的检查,以获得几乎相同的行为:

class Person
  def inspect
    vars = self.instance_variables.
      map{|v| "#{v}=#{instance_variable_get(v).inspect}"}.join(", ")
    "<#{self.class}: #{vars}>"
  end
end

但您也可以安装一个名为 awesome_print 的 gem,它会提供非常好的输出。 首先在控制台中:

$ gem install awesome_print

然后在 irb 或您的脚本中:

require 'awesome_print'
ap Person.new("John")

还有一个内置的pp 库,具有类似的用途。它仍然不能免疫(至少在 Ruby 1.9.2-p290 中)覆盖to_s。

pp 的简单示例:

require 'pp'
pp Person.new("John")

【讨论】:

  • 谢谢,但是... p(或 puts)不会原样给出相同的结果,并且您的代码会给出以下结果,因此 to_s 修饰符不再起作用.. #<0x1d2e6d8>
【解决方案2】:

我相信默认检查使用 to_s 方法。

但是,您也可以覆盖检查,包括在覆盖之前将旧的 to_s 方法别名。

【讨论】:

  • 您想使用 alias_method(查找它)来“保存”旧 to_s 方法的副本,然后重写 inspect 方法以简单地调用旧 to_s 方法。您也可以在覆盖 to_s 之前使用 alias_method 将旧 to_s 别名为“检查”——如果可行,这是最简单的方法。
  • 这不起作用佩里,尝试了 alias 和 alias_method 的所有组合,要么你得到两者的美化输出,要么得到两者的非美化输出
  • 我提出的建议基本上与其他回答的人给出的相同,但有一些小的变化。我认为您可能做得不太对 - 也许您应该发布您尝试过的代码。
  • 这不起作用。默认的#to_s 不包括实例变量。
【解决方案3】:

Perry,这是我尝试让 alias 或 alias_method 工作的众多组合之一,如果你认为这有效,你能提交一个有效的 sn-p 吗?

class Person
  attr_accessor :first, :last, :birthdate
  def initialize(first=nil, last=nil, birthdate=nil)
    @first, @last, @birthdate = first, last, birthdate
  end

  alias old_to_s to_s
  def inspect
    old_to_s
  end
  def to_s
    "#{@first} #{@last}"
  end

end

me = Person.new("Peter")
p me         #-->#<Person:0x1da26b8>
p me.inspect #-->"#<Person:0x1da26b8>"

【讨论】:

    【解决方案4】:

    这里是 Aleksander 的解决方案,第一个 p 应该给出被覆盖的 to_s 但没有,你必须像第二个 p 一样显式调用 to_s 以获得我想要的结果。我也不确定这个检查是否会给出与原始检查相同的结果,但对象更复杂,我会在家里试试。谁还有更好的镜头? 我知道 awesome_print gem,但恕我直言,对于像这样的基本内容,您不需要包含 gem。

    class Person
    
      def initialize(first=nil, last=nil, birthdate=nil) 
        @first, @last, @birthdate = first, last, birthdate 
      end 
    
      # New implementation of to_s 
      def to_s 
        "#{@first} #{@last}" 
      end
    
      def inspect 
        vars = self.instance_variables. 
          map{|v| "#{v}=#{instance_variable_get(v).inspect}"}.join(", ") 
        "<#{self.class}: #{vars}>" 
      end 
    
    end 
    
    me = Person.new("John")
    p me         #--><Person: @first="John", @last=nil, @birthdate=nil>
    p me.to_s    #-->"John "
    p me.inspect #-->"<Person: @first=\"John\", @last=nil, @birthdate=nil>"
    

    编辑:我刚刚尝试了 awesome_print,我很高兴,害怕我不得不使用 IRB 来使用它,但事实并非如此,我在我的编辑器中运行我的脚本

    require 'awesome_print'
    ap me --> 
    #<Person:0x027efe20
        @birthdate = nil,
        @first = "John",
        @last = nil
    >
    

    使用 Aleksander 建议的 pretty_print 进行编辑,不会干扰覆盖的 to_s

    require 'PP'
    class Person
    
      def initialize(first=nil, last=nil, birthdate=nil) 
        @first, @last, @birthdate = first, last, birthdate 
      end 
    
      # New implementation of to_s 
      def to_s 
        "#{@first} #{@last}" 
      end
    end 
    
    me = Person.new("John")
    pp me                   #--> <Person: @first="John", @last=nil, @birthdate=nil>
    warn me.pretty_inspect  #--> <Person: @first="John", @last=nil, @birthdate=nil>
    

    【讨论】:

    • Re: awesome_print 还有 pp (pretty_print) 库,它与 Ruby 捆绑并具有类似的用途。
    • 但是pp是基于inspect的,所以改变to_s会破坏pp的结果。 awesome_print 不会发生这种情况,这就是我推荐使用它的原因。
    • 试过 pp 和 pretty_inspect,它们不会与被覆盖的 to_s 相互影响,它在内核中很好,但你必须要求它,编辑答案以供将来参考并标记您的回复作为答案,感谢您的帮助
    • 奇怪-我尝试了您提供的示例,但结果不同。您使用哪个 Ruby 版本?另外:你需要'PP'还是'pp'?
    • ruby -v 给出 ruby​​ 1.9.3p0 (2011-10-30) [i386-mingw32],require 'pp' 给出与 require 'PP' 相同的结果,会有区别吗?也许是操作系统,我使用的是 Windows 7
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多