Ruby 求解 singleton_class, singleton_methods 的深入问题

breeze · November 29, 2016 · Last by jude replied at December 03, 2016 · 2781 hits

环境:ruby2.3.2

问题:
  1. 为什么下面两者的 ancestors 差这么多,A 与 A.singleton_class 有哪些关联与不同?
  2. 与 A.ancestors 相比 A.singleton_class.ancestors 中 #<Class:A>, #<Class:Object>类似这些东西是什么?
class A
end

A.ancestors
# => [A, Object, Kernel, BasicObject]

A.singleton_class.ancestors
# => [#<Class:A>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 
问题:
  1. 为什么不是一个 singleton_class 也可以定义 singleton_method ?
  2. 现在可以推出,A 与 A.singleton_class 是完全不同的东西,那么 A.singleton_class 存在的意义是什么?
class A
  def self.say
    p 'say'
  end
end

A.singleton_methods
# => [:say]

A.singleton_class.singleton_methods
# => [:constants, :nesting]   方法中没有:say,这个可以理解,因为它的ancestors中没有A。

A.singleton_class?
#  => false

A.singleton_class.singleton_class?
#  => true
问题:
  1. 如果说 A.say 是一个 singleton_method,也是一个类方法,那么 a1.run 是一个 singleton_method,也是一个类方法?
  2. 类方法与 singleton_method 有什么关系与不同?
class A
  def self.say
    p 'say'
  end
end

A.singleton_methods
# => [:say]

a1 = A.new

def a1.run
  p 'run'
end

a1.singleton_methods
# => [:run] 
问题:
  1. #<Class:#<A:0x000000010ac200>> 这是什么东西?实例变量也是一个类?
class A
  def self.say
    p 'say'
  end
end

a1 = A.new
#  => #<A:0x000000010ac200>

a1.singleton_class.ancestors
#=> [#<Class:#<A:0x000000010ac200>>, A, Object, Kernel, BasicObject]

a1.singleton_class.singleton_methods
#  => [:say]  # 因为ancestor中有 A, 所以继承了:say方法

In Ruby, everything is an object.

在 ruby 中类既是对象也是一个类, 对你第一个问题 A.ancestore 针对的是 A.new 出来的对象的祖先链。A.Singleton_class.ancestors 针对的是 A 这个类对象的祖先链。问题二:那些在 ruby 里面叫元类,ruby 里面的类都有元类。 A.singleton_class 里面定义的是 A 这个类的方法。 a1.run 这个方法是 a1 的单件方法,他是在 a1 的元类里面定义的。最后一个表示的是一个对象的元类 ruby 必看书"ruby 元编程"还有新出的书"ruby 源码剖析"

要了解单件,建议先学 javascript

理解“类”也是一个对象是关键

class A; end
等价于
A = Class.new # 看到了么?创建一个“类”跟创建一个对象 ( a = A.new ) 是一样的

#4 楼 @piecehealth 你们说的这些,我都懂,感觉和问题但无关。
就像 A 是 Class.new 出来的对象,和 A.singleton_class 也是 Class.new 出来的对象。
但是 A 与 A.singleton_class 有什么相同 与 不同点呢?

#5 楼 @breeze 你 Class.new 一个 A.singleton_class 看看

理解 singleton_class 的话应该站在对象的角度思考,这是一个专属于某个实例的特殊对象 用来描述这个实例的类方法

尝试回答一下:

1.为什么下面两者的 ancestors 差这么多,A 与 A.singleton_class 有哪些关联与不同?
2.与 A.ancestors 相比 A.singleton_class.ancestors 中 #<Class:A>, #<Class:Object>类似这些东西是什么?

首先要明白 ancestors 返回的是一个类的祖先链上的其他类(也包含自身),通过不断地调用 supercalss 方法就能遍历祖先链(例外情况是 Kernel):

A.superclass #=> Object
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil

然后要明白 singleton_class 是什么东西。它是一个隐藏在对象(不管是普通对象还是类)后面的一个特殊类,它只有一个实例(就是它自己)。既然 singleton_class 是一个类,那么调用它的 ancestors 方法就会返回它祖先链上的其他类。

A.singleton_class.superclass #=> #<Class:Object>
A.singleton_class.superclass.superclass #=> #<Class:BasicObject>

#<Class:Object> 其实就是 Object 这个类的 singleton_class。

直到 Object 为止,一个类的 ancestors 跟它的 singleton_class 的 ancestors 是一一对应的,但是:

BasicObject.singleton_class.superclass #=> Class 
Class.superclass #=> Module
Module.superclass #=> Object
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil

所以一个类的 singleton_class 的祖先链会比这个类的祖先链要长。

一个对象的 singleton_class 跟这个对象的关联,我觉得是 singleton_class 用来保存定义在对象身上的方法(这是 singleton_class 存在的意义);不同之处在于,如果这个对象是普通对象,它是不能保存方法的,如果这个对象是类,它可以保存类的实例方法;不管何种情况,它的 singleton_class 保存的都是定义在对象身上的方法。

1.为什么不是一个 singleton_class 也可以定义 singleton_method ?

只要是个对象,都可以定义它的 singleton_method 方法。定义的方式有下面几种,实际效果都是一样的:

# 直接在对象上定义

class A; end
def A.p; puts 'A'; end
a = A.new
def a.p; puts 'a'; end

# 在类中定义(1)
class A
  def self.p; p 'A'; end
end
# 在类中定义(2)
class A
  def A.p; p 'A'; end
end
# 在类中定义(3)
class A
  class << self
    def p; p 'A'; end
  end
end

ps. 其他问题好像都已经顺便解释了。。。强烈建议看看《Ruby 元编程》这本书,看完就弄明白这些问题了。

You need to Sign in before reply, if you don't have an account, please Sign up first.