клас << самодійна ідіома в Рубі


873

Що class << selfробити в Рубі ?


35
Є дуже приємна стаття на цю тему, яку написали Єгуда Кац: yehudakatz.com/2009/11/15/… та Yugui: yugui.jp/articles/846
Андрій

3
Ще одна супер приємна стаття тут: integralist.co.uk/posts/eigenclass.html
Саман Мохамаді

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«s одноелементний клас, так що методи можуть бути перевизначені для поточного 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відкривається клас Singleton для цього об'єкта; одне використання цього полягає у впровадженні державної машини бідної людини:

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
Marc-André Lafortune

4
Це дуже заплутане посилання на a's, singleton_classоскільки a' s class (після зміни inspect) є унікальним варіантом Stringкласу. Якби він міняв Stringклас однотонних, це впливало б на всі інші Stringекземпляри. Що ще дивніше, це те, що якщо ви пізніше повторно відкриєте Stringдля повторного визначення, inspectто aвсе одно виберете нові зміни.
Старий Про

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

5
Я вважаю, що вираз "відкриває однокласний клас об'єкта" - який я багато разів раніше читав - неясним. Наскільки мені відомо, ніде в документах Ruby «не відкривається» клас, визначений, хоча ми всі маємо уявлення про те, що це означає. Чи class << selfозначає щось більше, ніж значення self, встановлене рівним класу синглтон в межах блоку?
Cary Swoveland

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

тепер ви можете визначити метод singleton, для 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 є однаковими, обидва вони існують в одиночному класі. ви можете використовувати foo.singleton_class.instance_methods(false)для перевірки.
Деймон Юань

22

Зазвичай, наприклад, методи є глобальними. Це означає, що вони доступні у всіх примірниках класу, за яким вони були визначені. На відміну від цього, метод одиночних реалізований на одному об'єкті.

Ruby зберігає методи у класах, і всі методи повинні бути пов'язані з класом. Об'єкт, для якого визначений однотонний метод, не є класом (це екземпляр класу). Якщо лише класи можуть зберігати методи, то як об’єкт може зберігати однотонний метод? Коли створено однотонний метод, Ruby автоматично створює анонімний клас для зберігання цього методу. Ці анонімні класи називаються метакласами, також відомими як однокласні класи або власні класи. Однотонний метод асоціюється з метакласом, який, у свою чергу, асоціюється з об'єктом, для якого був визначений метод синглтон.

Якщо в одному об'єкті визначено кілька однотонних методів, всі вони зберігаються в одному метакласі.

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 змінює поточне самоврядування, щоб вказати на метаклас об'єкта z1; тоді він визначає метод say_hello в метакласі.

Класи також є об'єктами (екземпляри вбудованого класу під назвою Class). Методи класу - це не що інше, як одиночні методи, пов'язані з об’єктом класу.

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

Усі об'єкти можуть мати метакласи. Це означає, що класи також можуть мати метакласи. У наведеному вище прикладі клас << self модифікує self, тому він вказує на метаклас класу Zabuton. Коли метод визначений без явного приймача (клас / об'єкт, на якому буде визначений метод), він неявно визначається в межах поточної області, тобто поточного значення self. Отже, метод начинки визначається в метакласі класу Забутон. Наведений вище приклад - це ще один спосіб визначення методу класу. IMHO, краще використовувати синтаксис 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«S #singleton_classмає всі методи екземпляра hi» и #classє, і , можливо , ще деякі ( :aтут).

[наприклад, методи речі #class і #singleton_class можуть бути застосовані безпосередньо до речі. коли рубін бачить thing.a, він спочатку шукає: визначення методу в thing.singleton_class.instan_methods, а потім у thing.class.instan_methods]


До речі - вони називають об'єкт однокласного класу == metaclass == eigenclass .


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

тест_2

тест_3


1

Насправді, якщо ви пишете будь-які розширення на C для своїх проектів Ruby, існує дійсно лише один спосіб визначити метод модуля.

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 з інших об'єктів, таких як Class і Module. Приклади з усіх відповідей приємно грати, але вам потрібно працювати з різними ідеями чи концепціями, щоб дійсно зрозуміти, що відбувається з тим, як написано код. Отже, зараз у вас є багато термінів.

Singleton, Class, Module, self, Object та Eigenclass виховувались, але Ruby так не називає Object Models. Це більше схоже на метаклас. Річард або __ чому показує вам ідею тут. http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html А якщо вас ударить, то спробуйте пошукати модель Ruby Object у пошуку. Два відео, про які я знаю на YouTube, - Дейв Томас та Пітер Купер. Вони також намагаються пояснити це поняття. Дейву знадобилося багато часу, щоб його отримати, тому не хвилюйтеся. Я все ще працюю над цим. Чому б інакше я був тут? Дякуємо за запитання Погляньте також на стандартну бібліотеку. Він має модуль Singleton так само, як і FYI.

Це досить добре. https://www.youtube.com/watch?v=i4uiyWA8eFk

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.