在Ruby中,该方法puts是Kernel模块的单例方法。
通常,当一个模块是另一个模块的included或extended时,该模块(而不是其单例类)将被添加到继承树中。这有效地使模块的实例方法可用于该模块或其单例类(分别用于include和extend)...但是混合模块的单例方法仍然不可访问,因为模块的单例类不是曾经添加到继承树中。
那为什么要使用puts(和其他内核单例方法)?
Kernel.singleton_methods(false)
# => [:caller_locations, :local_variables, :require, :require_relative, :autoload, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :test, :warn, :autoload?, :fork, :binding, :exit, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :URI, :eval, :iterator?, :block_given?, :catch, :throw, :loop, :gets, :sleep, :proc, :lambda, :trace_var, :untrace_var, :at_exit, :load, :Rational, :select, :Complex, :syscall, :open, :printf, :print, :putc, :puts, :readline, :readlines, :`, :p, :system, :spawn, :exec, :exit!, :abort, :set_trace_func, :rand, :srand, :trap, :caller]
Run Code Online (Sandbox Code Playgroud)
请注意,这puts 似乎不是以下对象的实例方法Kernel:
Kernel.instance_methods.grep(/puts/)
# []
Run Code Online (Sandbox Code Playgroud)
虽然Object包括Kernel
Object.included_modules
# [Kernel]
Run Code Online (Sandbox Code Playgroud)
据我所知,Kernel单例类(#<Class:Kernel>)不会出现在任何对象的祖先中。is_a?似乎对此表示同意:
Object.is_a? Class.singleton_class # false
Object.is_a? Kernel.singleton_class # false
Object.singleton_class.is_a? Class.singleton_class # true
Object.singleton_class.is_a? Kernel.singleton_class # false
Run Code Online (Sandbox Code Playgroud)
但是,由于某种原因,它们对于每个对象都显示为私有方法。
Object.puts "asdf"
# NoMethodError (private method `puts' called for Object:Class)
Run Code Online (Sandbox Code Playgroud)
如果#<Class:Kernel>祖先链中没有出现,方法查找将如何找到这些方法?
有关:
#<Class:Class>继承自#<Class:Module>您正在寻找错误的位置。
方法,如Kernel#Array,Kernel#Complex,Kernel#Float,Kernel#Hash,Kernel#Integer,Kernel#Rational,Kernel#String,Kernel#__callee__,Kernel#__dir__,Kernel#__method__,Kernel#`,Kernel#abort,Kernel#at_exit,Kernel#autoload,Kernel#autoload?,Kernel#binding,Kernel#block_given?,Kernel#callcc,Kernel#caller,Kernel#caller_locations,Kernel#catch,Kernel#eval,Kernel#exec,Kernel#exit,Kernel#exit!,Kernel#fail,Kernel#fork,Kernel#format,Kernel#gets,Kernel#global_variables,Kernel#initialize_clone,Kernel#initialize_copy,Kernel#initialize_dup,Kernel#iterator?,Kernel#lambda,Kernel#load,Kernel#local_variables,Kernel#loop,Kernel#open,Kernel#p,Kernel#pp,Kernel#print,Kernel#printf,Kernel#proc,Kernel#putc,Kernel#puts,Kernel#raise,Kernel#rand,Kernel#readline,Kernel#readlines,Kernel#require,Kernel#require_relative,Kernel#select,Kernel#set_trace_func,Kernel#sleep,Kernel#spawn,Kernel#sprintf,Kernel#srand,Kernel#syscall,Kernel#system,Kernel#test,Kernel#throw,Kernel#trace_var,Kernel#trap,Kernel#untrace_var,并Kernel#warn没有做任何事情与他们的接收器有效载荷。他们不调用私有方法,不访问实例变量,实际上它们完全忽略了什么self。
因此,如果您这样称呼他们,将会产生误导:
foo.puts 'Hello, World!'
Run Code Online (Sandbox Code Playgroud)
因为读者可能会误以为用来puts做某事foo,而实际上却完全忽略了它。(这适用特别是对的方法的印刷家庭,因为还存在着IO#puts和朋友,这的确做他们的接收器服务。)
因此,为了防止您误以接收器调用这些方法,将它们制成private,这意味着只能在没有显式接收器的情况下调用它们。(显然,它们仍将被调用self,但至少在视觉上不会如此明显。)
从技术上来说,这些都不是真正的方法在所有的,他们的行为更像程序,但Ruby没有手续,所以这是“假”的最佳途径。
之所以将它们也定义为单例方法,是因为您仍可以在Kernel不在继承层次结构中的上下文中调用它们,例如:
class Foo < BasicObject
def works
::Kernel.puts 'Hello, World!'
end
def doesnt
puts 'Hello, World!'
end
end
f = Foo.new
f.works
# Hello, World!
f.doesnt
# NoMethodError: undefined method `puts' for #<Foo:0x00007f97cf918ed0>
Run Code Online (Sandbox Code Playgroud)
根本不需要单独定义它们的原因是实例方法版本为private。如果不是,则Kernel.puts无论如何您都可以调用,因为Objectinclude Kernel和Kernelis的实例Module是的子类Object,因此Kernel是其本身的间接实例。但是,方法是 private,因此您将获得
foo.puts 'Hello, World!'
Run Code Online (Sandbox Code Playgroud)
代替。因此,它们需要单独复制。实际上,有一个辅助方法可以做到这一点Module#module_function。(这也用于Math,在那里你可以调用例如Math.sqrt(4)或include Math; sqrt(4)。在这种情况下,你的选择include荷兰国际集团Math或没有,而Kernel被预included中Object始终。)
因此,在总结:方法被复制为private 实例的方法Kernel以及public 单的方法(这是真的只是实例方法Kernel的单例类)。之所以将它们定义为private实例方法,是因为它们不能被显式的接收者调用,并且被迫看起来更像过程。将它们复制为的单例方法的原因是,只要在继承层次结构中不可用的上下文中,只要显式接收者为Kernel,就可以用显式接收者调用它们。KernelKernel
看一下这个:
class Foo < BasicObject
def works
::Kernel.puts 'Hello, World!'
end
def doesnt
puts 'Hello, World!'
end
end
f = Foo.new
f.works
# Hello, World!
f.doesnt
# NoMethodError: undefined method `puts' for #<Foo:0x00007f97cf918ed0>
Run Code Online (Sandbox Code Playgroud)