класс << идиома в Ruby


873

Что делает class << selfв Ruby ?


35
Иегуда Кац написал замечательную статью на эту тему: yehudakatz.com/2009/11/15/… и Yugui: yugui.jp/articles/846
Андрей

3
Другой супер хорошая статья здесь: integralist.co.uk/posts/eigenclass.html
Саман Mohamadi

2
Я вижу это внутри модуля, это делает его другим? github.com/ruby/rake/blob/master/lib/rake/rake_module.rb
Уильям Энтрикен

@FullDecent Это не имеет значения, так как все в Ruby является объектом, включая модули и классы.
Аарон

Ответы:


912

Во-первых, class << fooсинтаксис открывает fooодноэлементный класс (собственный класс). Это позволяет вам специализировать поведение методов, вызываемых для этого конкретного объекта.

a = 'foo'
class << a
  def inspect
    '"bar"'
  end
end
a.inspect   # => "bar"

a = 'foo'   # new object, new singleton class
a.inspect   # => "foo"

Теперь, чтобы ответить на вопрос: class << selfоткрывается selfодноэлементный класс, так что методы могут быть переопределены для текущего selfобъекта (который внутри тела класса или модуля является самим классом или модулем ). Обычно это используется для определения методов класса / модуля («статических»):

class String
  class << self
    def value_of obj
      obj.to_s
    end
  end
end

String.value_of 42   # => "42"

Это также может быть записано как сокращение:

class String
  def self.value_of obj
    obj.to_s
  end
end

Или даже короче

def String.value_of obj
  obj.to_s
end

Когда внутри определения функции, selfотносится к объекту, с которым вызывается функция. В этом случае class << selfоткрывается одноэлементный класс для этого объекта; Одно из применений этого заключается в реализации конечного автомата бедного человека:

class StateMachineExample
  def process obj
    process_hook obj
  end

private
  def process_state_1 obj
    # ...
    class << self
      alias process_hook process_state_2
    end
  end

  def process_state_2 obj
    # ...
    class << self
      alias process_hook process_state_1
    end
  end

  # Set up initial state
  alias process_hook process_state_1
end

Таким образом, в приведенном выше примере каждый экземпляр StateMachineExampleимеет process_hookпсевдоним process_state_1, но обратите внимание, как в последнем он может переопределить process_hook( selfтолько, не затрагивая другиеStateMachineExample экземпляры) process_state_2. Таким образом, каждый раз, когда вызывающая processсторона вызывает метод (который вызывает переопределение process_hook), поведение меняется в зависимости от того, в каком состоянии он находится.


22
@ Jörg: +1 за редактирование (я хочу, чтобы SO предоставил возможность вносить изменения, да ладно). Это действительно более распространенное использование class << selfдля создания методов класса / модуля. Я, вероятно, остановлюсь на этом использовании class << self, поскольку это гораздо более идиоматическое использование.
Крис Шестер-Янг

4
gsub! ("eigenclass", "singleton class"), см. предстоящий метод redmine.ruby-lang.org/repositories/revision/1?rev=27022
Марк-Андре Лафортун

4
Это действительно запутанным для обозначения a«S , singleton_classтак как a» класса s (после изменения inspect) представляет собой уникальный вариант Stringкласса. Если бы он менял Stringкласс синглтона, это затронуло бы все другие Stringэкземпляры. Что еще более странно, так это то, что если вы позже откроете Stringзаново, inspectто aвсе равно поймете новые изменения.
Old Pro

1
@OldPro Я все еще предпочитаю имя eigenclass, как (я полагаю) Матц также делает. Но я не могу угодить всем, я думаю.
Крис Джестер-Янг

5
Я нахожу выражение «открывает одноэлементный класс объекта» - которое я читал много раз раньше - расплывчатым. Насколько мне известно, нигде в документации по Ruby не «открывается» определенный класс, хотя у всех нас есть представление о том, что это значит. Означает ли class << selfчто-либо большее, чем значение, selfустановленное равным одноэлементному классу в области видимости блока?
Кэри Свовеланд

34

Я нашел супер простое объяснение о том class << self, Eigenclassи различных типах методов.

В Ruby есть три типа методов, которые можно применять к классу:

  1. Методы экземпляра
  2. Синглтон методы
  3. Методы класса

Методы экземпляра и методы класса почти аналогичны их одноименным в других языках программирования.

class Foo  
  def an_instance_method  
    puts "I am an instance method"  
  end  
  def self.a_class_method  
    puts "I am a class method"  
  end  
end

foo = Foo.new

def foo.a_singleton_method
  puts "I am a singletone method"
end

Другой способ доступа к Eigenclass(который включает одноэлементные методы) заключается в следующем синтаксисе ( class <<):

foo = Foo.new

class << foo
  def a_singleton_method
    puts "I am a singleton method"
  end
end

Теперь вы можете определить одноэлементный метод, для selfкоторого является сам класс Fooв этом контексте:

class Foo
  class << self
    def a_singleton_and_class_method
      puts "I am a singleton method for self and a class method for Foo"
    end
  end
end

4
на самом деле методы Singleton и методы Class одинаковы, оба они существуют в классе singleton. Вы можете использовать, foo.singleton_class.instance_methods(false)чтобы проверить.
Дэймон Юань

22

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

Ruby хранит методы в классах, и все методы должны быть связаны с классом. Объект, для которого определен одноэлементный метод, не является классом (это экземпляр класса). Если только классы могут хранить методы, как объект может хранить одноэлементный метод? При создании одноэлементного метода Ruby автоматически создает анонимный класс для хранения этого метода. Эти анонимные классы называются метаклассами, также известными как одноэлементные классы или собственные классы. Метод singleton связан с метаклассом, который, в свою очередь, связан с объектом, для которого был определен метод singleton.

Если в одном объекте определены несколько одноэлементных методов, все они хранятся в одном метаклассе.

class Zen
end

z1 = Zen.new
z2 = Zen.new

class << z1
  def say_hello
    puts "Hello!"
  end
end

z1.say_hello    # Output: Hello!
z2.say_hello    # Output: NoMethodError: undefined method `say_hello'…

В приведенном выше примере класс << z1 изменяет текущее значение self на метакласс объекта z1; затем он определяет метод say_hello в метаклассе.

Классы также являются объектами (экземплярами встроенного класса под названием Class). Методы класса - это не более чем одноэлементные методы, связанные с объектом класса.

class Zabuton
  class << self
    def stuff
      puts "Stuffing zabuton…"
    end
  end
end

Все объекты могут иметь метаклассы. Это означает, что у классов также могут быть метаклассы. В приведенном выше примере class << self изменяет self, поэтому оно указывает на метакласс класса Zabuton. Когда метод определен без явного получателя (класс / объект, для которого будет определен метод), он неявно определяется в текущей области, то есть в текущем значении себя. Следовательно, метод stuff определен в метаклассе класса Zabuton. Приведенный выше пример является еще одним способом определения метода класса. ИМХО, лучше использовать синтаксис def self.my_new_clas_method для определения методов класса, так как это облегчает понимание кода. Приведенный выше пример был включен, чтобы мы понимали, что происходит, когда сталкиваемся с синтаксисом класса << self.

Дополнительную информацию можно найти в этом посте о Ruby Classes .


15

Что делает класс << вещь:

class Hi
  self #=> Hi
  class << self #same as 'class << Hi'
    self #=> #<Class:Hi>
    self == Hi.singleton_class #=> true
  end
end

[это делает self == thing.singleton_class в контексте своего блока] .


Что такое thing.singleton_class?

hi = String.new
def hi.a
end

hi.class.instance_methods.include? :a #=> false
hi.singleton_class.instance_methods.include? :a #=> true

hiобъект наследует его #methodsот своего, #singleton_class.instance_methodsа затем от своего #class.instance_methods.
Здесь мы дали hi«s одноэлементный класс метод экземпляра :a. Это можно было сделать с помощью класса << hi .
hi«S #singleton_classимеет все методы экземпляра hi» ы #classесть, и , возможно , еще некоторые ( :aздесь).

[методы экземпляра вещи #class и #singleton_class могут быть применены непосредственно к вещи. когда ruby ​​видит thing.a, он сначала ищет: определение метода в thing.singleton_class.instance_methods, а затем в thing.class.instance_methods]


Кстати, они называют синглтон-класс объекта == метакласс == собственный класс .


3

А Синглтон метод это метод , который определен только для одного объекта.

Пример:

class SomeClass
  class << self
    def test
    end
  end
end

test_obj = SomeClass.new

def test_obj.test_2
end

class << test_obj
  def test_3
  end
end

puts "Singleton's methods of SomeClass"
puts SomeClass.singleton_methods
puts '------------------------------------------'
puts "Singleton's methods of test_obj"
puts test_obj.singleton_methods

Синглтон методы SomeClass

тестовое задание


Синглтон методы test_obj

test_2

test_3


1

Фактически, если вы пишете какие-либо расширения C для своих проектов Ruby, на самом деле есть только один способ определить метод Module.

rb_define_singleton_method

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

Объекты первыми.

foo = Object.new

Могу ли я сделать метод для Foo?

Конечно

def foo.hello
 'hello'
end

Что мне с этим делать?

foo.hello
 ==>"hello"

Просто еще один объект.

foo.methods

Вы получаете все методы Object плюс ваш новый.

def foo.self
 self
end

foo.self

Просто объект Foo.

Попробуйте посмотреть, что произойдет, если вы создадите foo из других объектов, таких как класс и модуль. С примерами из всех ответов приятно играть, но вам нужно работать с разными идеями или концепциями, чтобы действительно понять, что происходит с тем, как написан код. Так что теперь у вас есть много терминов, чтобы пойти посмотреть.

Singleton, Class, Module, self, Object и Eigenclass были подняты, но Ruby не называет объектные модели таким образом. Это больше похоже на метакласс. Ричард или __ почему показывает вам идею здесь. http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html И если вас унесет, попробуйте поискать Ruby Object Model в поиске. Два видео, которые я знаю на YouTube, это Дейв Томас и Питер Купер. Они тоже пытаются объяснить эту концепцию. Дэйву понадобилось много времени, чтобы получить его, так что не волнуйтесь. Я все еще работаю над этим тоже. Зачем еще я буду здесь? Спасибо за ваш вопрос. Также взгляните на стандартную библиотеку. Он имеет одноэлементный модуль, как FYI.

Это довольно хорошо. https://www.youtube.com/watch?v=i4uiyWA8eFk

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.