Каковы * реальные * шаги в поиске метода ruby? - PullRequest
0 голосов
/ 11 мая 2018

Я прочитал сообщения stackoverflow по этой теме, а также несколько статей, которые включают Учебник по Ruby Method Lookup , Что такое путь поиска метода в Ruby .Кроме того, я проверил главу об объектной модели в Ruby Metaprogramming 2 , спросил в нескольких чатах и ​​создал эту ветку reddit .Если не считать C, я сделал все возможное, чтобы это выяснить.

Как описано выше, эти 6 мест проверяются (по порядку) во время поиска метода на принимающем объекте, таком как fido_instance :

  1. синглтон-класс fido_instance
  2. IClass (из расширенного модуля)
  3. IClass (из предварительно добавленного модуля)
  4. class
  5. IClass (из включенного модуля)
  6. суперкласс (если метод здесь не найден, повторите шаги 4-6)

Очевидно, диаграмманеполные, и все эти одноэлементные классы, возможно, не были созданы в реальном мире.Тем не менее, эти 6 шагов оставляют желать лучшего и не охватывают следующий сценарий.Если над классом синглтона fido_instance не было расширенного / добавленного IClass, то нет объяснения, выполняется ли шаг 4 для класса синглтона fido_instance.Я должен предположить, что нет, так как весь поиск метода был бы коротким замыканием.

Если бы я угадал набор шагов, которые могли бы объяснить поведение поиска метода ruby, это могло бы выглядеть так:

  1. проверьте fido_instance.class для метода.(очевидно, что ruby ​​не собирается использовать свой собственный метод #class для поиска метода, но он передает логику процесса)
  2. проверка fido_instance.class.superclass для метода.Продолжайте добавлять .superclass и проверяйте метод, пока не останется суперклассов.(опять же, ruby ​​не будет использовать свой собственный метод #superclass)
  3. метод не найден.Начните с шага 1, ищите #method_missing на этот раз.

Я также вспоминаю, что читал, что есть отдельный процесс поиска метода, если принимающий объект является классом, но я не могу вспомнить, где.

Так, каково правильное, подробное объяснение, которое не включает знание C?

1 Ответ

0 голосов
/ 08 октября 2018

В этом втором ссылочном тексте есть ... жемчужина, которая, я думаю, доходит до сути ответа: предки синглтон-класса.Применительно к вашему объекту это будет:

fido_instance.singleton_class.ancestors

Это всегда даст вам порядок поиска методов, который использует Ruby.Это довольно просто, когда вы смотрите на это таким образом, и это основной ответ на ваш вопрос.Ruby начнет с singleton_class и продолжит свой путь к предкам, ищущим этот метод.Использование вашей диаграммы:

fido.singleton_class.ancestors
=> [Fetch, WagTail, DogClass, Object, Kernel, BasicObject]

(Примечание 1: Bark не является частью этого вывода, потому что вы использовали extend вместо include. Подробнее об этом в секунду.)

(Примечание 2: Если он не находит его полностью вплоть до BasicObject, то он вызовет method_missing по той же цепочке предков.)

При вызове метода в классе нет разницыпотому что в Ruby класс это просто экземпляр класса Class.Так что DogClass.method1 будет искать method1 на DogClass.singleton_class, а затем подниматься по цепочке предков, как и раньше.

DogClass.singleton_class.ancestors
=> [Bark, Class, Module, Object, Kernel, BasicObject]

Поскольку вы использовали extend для Bark, это то, где мы находимЭто!Так что если Bark определил метод bark, то вы можете вызвать DogClass.bark, потому что этот метод определен в предках DogClass singleton_class '.

Чтобы понять, что это дерево предков (вместополагаться на его распечатку каждый раз), вам просто нужно знать, как наследование изменяется подклассами, extend, include, prepend и т. д.

  1. Подклассы дают ребенкуКласс всей цепочки предков своего суперкласса.
  2. include модуль в классе C добавляет этот модуль в цепочку предков после C и перед всем остальным.
  3. prepend модуль в классе C добавляет этот модуль в цепочку предков раньше всего, включая C и любые в настоящее время предварительно добавленные модули.
  4. def x.method1 добавляет method1 к x.singleton_class.Точно так же x.extend(M) добавит M к происхождению x.singleton_class (но не к x.class).Обратите внимание, что последнее именно то, что произошло с Bark и DogClass.singleton_class, но может в равной степени применяться к любому объекту.

Исключение extend из приведенного выше списка, поскольку оно не изменяет объектродословная.Он изменяет происхождение singleton_class этого объекта - как мы видели, Bark был включен в DogClass.singleton_class.ancestors.


Tangent:

Немного о методах класса вышеэто ключ для меня, чтобы понять, насколько важны синглтон-классы для Ruby.Очевидно, вы не можете определить bark для DogClass.class, потому что DogClass.class == Class, и мы не хотим bark для Class!Итак, как мы можем позволить DogClass быть экземпляром Class, позволяя ему иметь (class) метод bark, который определен для DogClass, но не относящиеся к нему классы?Используя синглтон-класс!Таким образом, определение «метода класса», например, def self.x внутри класса C, похоже на C.singleton_class.send(:define_method, :x) {...}.

...