关于红宝石“主要”物体的不一致性

时间:2012-07-13 16:33:50

标签: ruby metaprogramming

Ruby对类和对象非常一致。但是,当涉及到顶级方法声明时,该规则会以某种方式发生故障。例如,

$ puts self # => main
$ puts self.class # => Object

但是,在此main对象的上下文中声明的方法在某种程度上可用作类Object的私有方法。

这有什么合理的解释吗?我知道这非常方便,因为这些方法看起来像“独立函数”,但是把它当作“它就是这样”会打破其他方面一致的规则,因为通常你必须在类的上下文中定义一个方法因为它是该类的一种方法。但是,main对象不是类Object,也不是类。

重新构思我的问题: 在ruby的REPL中声明方法的范围是什么? {class Object / object main或module Kernel}

关于在REPL中声明的方法的私有性,请参阅以下示例:

def my_method # "a method declared in REPL"
  puts "method called"
end

# calling my_method in REPL aka top-level scope
my_method # => "method called"
[].my_method # => private method `my_method' called for []:Array (NoMethodError)
Array.my_method # => private method `my_method' called for Array:Class (NoMethodError)

问:如果my_method被定义为Object类下的私有方法,那么为什么self被设置为main而不是class Object。如果有人说它不在私人范围内,那为什么我会得到上述错误?

3 个答案:

答案 0 :(得分:3)

虽然我不确定我是否完全理解这个问题,但我会尽力回答:

当你在irb内工作时,你真的在​​这样的环境中工作:

class Object
  # you're here
end

所以,如果你在REPL中声明一个方法,你实际上是在main的类中声明它:

ruby-1.9.3-rc1 :001 > def foo; end
 => nil 
ruby-1.9.3-rc1 :002 > self.class.public_methods.include?(:foo)
 => true 

我不确定你在哪里知道那里定义的方法是私有的(但同样,我可能会误解这个问题)。

您在irb内的课程包含Kernel模块,这就是您可以访问其功能的原因(因为您在课堂内,它们甚至可以显示为独立功能)虽然他们是常规方法):

ruby-1.9.3-rc1 :003 > self.class.included_modules
 => [Kernel] 

这会使它与典型的Ruby行为一致,实际上,main只是Object类中的self

如果您致电self,您将收到main,因为这是通过检查指示的方式。我们可以从#to_s中移除self方法并查看实际值:

ruby-1.9.3-p0 :001 > self
 => main 
ruby-1.9.3-p0 :002 > class << self; remove_method :to_s; end
 => #<Class:#<Object:0x007fc08387af00>> 
ruby-1.9.3-p0 :003 > self
 => #<Object:0x007fc08387af00 @prompt={:PROMPT_I=>"ruby-1.9.3-p0 :%03n > ", :PROMPT_S=>"ruby-1.9.3-p0 :%03n%l> ", :PROMPT_C=>"ruby-1.9.3-p0 :%03n > ", :PROMPT_N=>"ruby-1.9.3-p0 :%03n?> ", :RETURN=>" => %s \n", :AUTO_INDENT=>true}> 

编辑:在继续前进之前,我认为您应该澄清您使用的是哪个Ruby版本,请参阅下面的示例以获取1.9.3:

ruby-1.9.3-p0 :001 > def my_method
ruby-1.9.3-p0 :002?>     puts "method called"
ruby-1.9.3-p0 :003?> end
 => nil 
ruby-1.9.3-p0 :004 > 
ruby-1.9.3-p0 :005 >   [].my_method
method called
 => nil 
ruby-1.9.3-p0 :006 > Array.my_method 
method called

答案 1 :(得分:2)

IMO顶级方法在Kernel模块的上下文中定义,而不是Object类,因此它们最终都在Object实例和类方法中(因为Class类也包含Kernel):

def a_method
  "inside a_method"
end

public :a_method

p Object.a_method
p Object.new.a_method
p self.a_method
p Kernel.a_method

允许它们在任何上下文中使用。

更新:更简单,Module.superclassObject,因此当我们在Object中定义某些内容时,它最终会以Module结尾实例方法也是如此。 Class.superclassModule,所以它最终也是所有类的类方法。由于Kernel.classModuleKernel.a_method是类Module的实例方法。嗯,真的很简单?

答案 2 :(得分:0)

如果你问为什么你可以在顶级定义一个方法/函数,而你不在任何一个类中,那么答案就是你也可以定义一个方法它所属的类的对象。我们来看一个例子。

class C
  def f
    def g
      'g'
    end
    'f'
  end
end

C.new.g # NoMethodError: undefined method `g' for #<C:0x0>
C.new.f # => 'f'
C.new.g # => 'g'

不建议这样做,但在这种情况下它是一致的。