2015-08-02 5 views
9

Прежде всего, я понимаю, что этот вопрос не имеет приложения в реальном мире, мне просто интересно.Вложенный метод поиска однопользовательского метода

Представьте, что есть класс с одноплодным методом:

class Foo 
    def self.bar 
    end 
end 

Если мы называем Foo.bar, это будет первый поиск для метода в одноплодном классе каждого предка Foo, а затем будет искать в классе ссылается на метод .class и его предки. Мы можем подтвердить, что с Foo.singleton_class.ancestors, который возвращает:

[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>, 
Class, Module, Object, Kernel, BasicObject] 

Но что произойдет, если мы имеем вложенную одноплодной класс, как:

class Foo 
    class << self 
    class << self 
     def bar 
     end 
    end 
    end 
end 

Если мы называем Foo.singleton_class.singleton_class.ancestors, он возвращает:

[#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, 
#<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, 
#<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 

Я не понимаю, как организована эта иерархия.

+0

Вы предполагаете, что это организовано вообще. Вы уверены, что это/должно быть организовано? –

+0

Возможно, вы можете уточнить свой вопрос? Вы спрашиваете, где определен метод бара во втором примере? – saghaulor

+0

Определяется метод бара. Я спрашиваю, как организована иерархия наследования во втором примере. –

ответ

16

Многое из этого объяснения базируется на How Ruby Method Dispatch Works Джеймсом Коглану, немного из Ruby Hacking Guide и просто smidge из source.

Начнет с кратким, родословная выглядит следующим образом:

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^      ^    | 
|        |       |    | 
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> | 
| ^     ^      ^    | 
|  |  Kernel   |       |    | 
|  |  ^   |       |    | 
|  |   |    | +-----------------------|----------------+ 
|  +-----+----+    | |      | 
|   |     | v      | 
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>> 
      ^    ^      ^
      |     |       | 
      Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>> 

---> Parent 
~~~> Singleton class 

Давайте начнем с самого начала и пристроить. BasicObject - это корень всего - если вы проверите BasicObject.superclass, вы получите nil. BasicObject также является экземпляром Class. Да, это циркуляр, и есть special case in the code, чтобы справиться с этим.Когда A является экземпляром B, A.singleton_class является потомком B, таким образом мы получаем это:

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject> 

Object наследует от BasicObject. Когда A наследуется от B, A является дочерним по отношению к B и A.singleton_class является дочерним по отношению к B.singleton_class. Object также включает Kernel. Когда A включает B, B вставлен в качестве первого предка A (после A сам, но до A.superclass).

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject 
    ^     ^
    |  Kernel   | 
    |  ^   | 
    |   |    | 
    +-----+----+    | 
      |     | 
     Object ~~~~~~> #<Class:Object> 

Kernel является экземпляром Module. Это единственный пример Module, который мы увидим, и его одноэлементный класс не появляется в каких-либо цепочках родословной, поэтому я не буду выходить за его пределы.

Теперь мы переходим к Foo, который наследуется от Object (хотя вам не нужно писать < Object). Мы уже можем выяснить, что такое Foo, а его одноэлементный класс - дети.

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject> 
    ^     ^
    |  Kernel   | 
    |  ^   | 
    |   |    | 
    +-----+----+    | 
      |     | 
     Object ~~~~~~> #<Class:Object> 
     ^    ^
      |     | 
     Foo ~~~~~~~~> #<Class:Foo> 

Теперь Class наследует от Module и Module унаследован от Object, поэтому добавьте Module и соответствующие классы одноплодной. Потому что Module < Object и Object < BasicObject и BasicObject.instance_of?(Class), вот где рисунок немного напуган. Помните, что вы просто прекращаете движение вверх, когда вы нажимаете BasicObject.

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^           | 
|        |           | 
| BasicObject ~~~~~> #<Class:BasicObject>         | 
| ^     ^           | 
|  |  Kernel   |           | 
|  |  ^   |           | 
|  |   |    | +----------------------------------------+ 
|  +-----+----+    | | 
|   |     | v 
+-------> Object ~~~~~~> #<Class:Object> 
      ^    ^
      |     | 
      Foo ~~~~~~~~> #<Class:Foo> 

Последний шаг. Каждый экземпляр Class имеет singleton_class (хотя он не будет создан до тех пор, пока он не понадобится, или вам понадобится больше ОЗУ). Все наши одноэлементные классы являются экземплярами Class, поэтому у них есть классы одноэлементного типа. Следите за этим предложением: родительский класс одного класса класса - это однопользовательский класс родительского класса. Я не знаю, есть ли краткий способ заявить, что в отношении систем типа, и Ruby source в значительной степени говорит, что он просто делает это для обеспечения согласованности в любом случае. Так что, когда вы просите Foo.singleton_class.singleton_class язык счастливо обязывает вас и распространяет необходимые родителям вверх, что приводит в конце концов к:

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^      ^    | 
|        |       |    | 
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> | 
| ^     ^      ^    | 
|  |  Kernel   |       |    | 
|  |  ^   |       |    | 
|  |   |    | +-----------------------|----------------+ 
|  +-----+----+    | |      | 
|   |     | v      | 
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>> 
      ^    ^      ^
      |     |       | 
      Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>> 

Если исходить из любого узла в этой графе и траверсы в глубину, справа налево (и остановитесь на BasicObject, вы получите цепочку предков узла, как мы и хотели. И мы построили его из основных аксиом, чтобы мы могли просто доверять ему. Недостаток доверия, есть пара интересных способов далее проверьте структуру.

Попробуйте найти node.singleton_class.ancestors - node.ancestors для любого узла на графике. Это дает нам предки одноэлементного класса, которые не являются предками самого узла, что устраняет некоторую запутанную избыточность в списке.

> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors 
=> [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, 
    #<Class:Class>, #<Class:Module>] 

Вы также можете проверить любого родителя с помощью node.superclass.

> Foo.singleton_class.singleton_class.superclass 
=> #<Class:#<Class:Object>> 

И вы можете даже проверить, что идентификатор объекта все соответствует, так что не анонимные классы выскакивают повсюду без особых отношений друг с другом.

> def ancestor_ids(ancestors) 
> ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")} 
> end 

> puts ancestor_ids(Foo.ancestors) 
70165241815140 Foo 
70165216040500 Object 
70165216040340 Kernel 
70165216040540 BasicObject 

> puts ancestor_ids(Foo.singleton_class.ancestors) 
70165241815120 #<Class:Foo> 
70165216039400 #<Class:Object> 
70165216039380 #<Class:BasicObject> 
70165216040420 Class 
70165216040460 Module 
70165216040500 Object # Same as Foo from here down 
70165216040340 Kernel 
70165216040540 BasicObject 

> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors) 
70165241980080 #<Class:#<Class:Foo>> 
70165215986060 #<Class:#<Class:Object>> 
70165215986040 #<Class:#<Class:BasicObject>> 
70165216039440 #<Class:Class> 
70165216039420 #<Class:Module> 
70165216039400 #<Class:Object> # Same as Foo.singleton_class from here down 
70165216039380 #<Class:BasicObject> 
70165216040420 Class 
70165216040460 Module 
70165216040500 Object 
70165216040340 Kernel 
70165216040540 BasicObject 

И что, в двух словах, как вы snipe a nerd.

+2

Это очень хорошее объяснение. –

3

#<Class:Foo> является собственным/анонимным классом данного класса Foo. Если это собственный/анонимный класс также был продлен еще один собственный класс получил бы создали - который таким образом получает представлен в виде #<Class:#<Class:Foo>>

Родителя собственного класса является собственным классом Object класса, родитель которого является собственным классом BasicObject. Аналогично, родительский класс собственного класса другого собственного класса является собственным классом собственного класса класса Object и т. Д.

Ниже кода в сочетании with this explanation дает больше понимания

p Foo.class 
p Foo.class.ancestors 
puts "-----------------" 
p Foo.singleton_class 
p Foo.singleton_class.ancestors 
puts "-----------------" 
p Foo.singleton_class.singleton_class 
p Foo.singleton_class.singleton_class.ancestors 

, который выводит

иерархию классов Эйген, как видно выше, будет повторять любое количество уровней. Например:

p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class 
puts "-----------------" 
p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class.ancestors 

Над кодом выходов

#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>> 
----------------- 
[#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:Object>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:BasicObject>>>>>, #<Class:#<Class:#<Class:#<Class:Class>>>>, #<Class:#<Class:#<Class:#<Class:Module>>>>, #<Class:#<Class:#<Class:#<Class:Object>>>>, #<Class:#<Class:#<Class:#<Class:BasicObject>>>>, #<Class:#<Class:#<Class:Class>>>, #<Class:#<Class:#<Class:Module>>>, #<Class:#<Class:#<Class:Object>>>, #<Class:#<Class:#<Class:BasicObject>>>, #<Class:#<Class:Class>>, #<Class:#<Class:Module>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 
+0

Хорошая работа. Я собирался ответить на это, но я хотел получить свою терминологию до начала. Ты подтолкнул меня на это. Вот почему я задавал вопросы, которые я задал здесь http://stackoverflow.com/questions/31775576/nested-singleton-class-method-lookup#comment51574477_31775576 ​​ Ответ, на который определяется метод, требует объяснения иерархии наследования. – saghaulor

+0

@saghaulor Спасибо за upvote и добрые слова –

Смежные вопросы