我正在学习 Ruby 中的元编程,并且正在尝试通过 method_missing 和 define_method 定义缺失的方法。我遇到了一些意想不到的行为,想知道是否有人可以解释这一点。这是我的类(class):
class X
def method_missing(m, *args, &block)
puts "method #{m} not found. Defining it."
self.class.send :define_method, m do
puts "hi from method #{m}"
end
puts "defined method #{m}"
end
end
现在,这段代码:
x = X.new
x.some_method
puts
x.some_method
puts
puts x
产生输出:
method some_method not found. Defining it.
defined method some_method
hi from method some_method
method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>
我不明白的是最后一部分:为什么 Ruby 在调用 puts 时调用 to_ary?为什么 Ruby 会尝试将我的对象转换为数组来打印它?
我用 Google 搜索并找到了这些相关链接:
- http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
- http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
这些也讨论了 method_missing 和 to_ary 陷阱,但没有具体说明为什么 puts 会调用 to_ary。
我还应该提到,当我定义一个 to_s 时,行为不会改变,例如
def to_s
"I'm an instance of X"
end
那么“puts x”的输出是:
method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
最佳答案
puts
是 $stdout.puts
的同义词。 $stdout 是一个 IO
类,因此请查看 IO.puts 的文档:
Writes the given objects to ios as with IO#print. Writes a record separator (typically a newline) after any that do not already end with a newline sequence. If called with an array argument, writes each element on a new line.
这意味着 puts
方法旨在写入多行输出。因此,它会尝试调用对象上的 to_ary
方法,如果定义了 to_ary
,则在新行上打印返回的 Array
的每个元素, else puts
调用 to_s
方法。
to_ary
内部用法在 Ruby 文档中确实没有很好的记录(Matz 在他的The Ruby Programming Language 书中指出了这一点)。
另一方面,print
和 p
方法不调用 to_ary
,只调用 to_s
。
旁注:有趣的是,to_ary
必须返回真正的Array
对象,而不是定义each
方法或其他:
class Test
def to_ary
10.downto(1)
end
end
puts Test.new
#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
# from (irb):28:in `puts'
# from (irb):28:in `puts'
# from (irb):28
关于Ruby:为什么 puts 会调用 to_ary?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8960685/