Self в Ruby

сентября 8, 2012  |  Published in Ruby, Расширения, Уголок философа  |  5 Comments

Честно сказать, достали меня уже вопросом о том, что такое self. Особено странно, что некоторые люди начинают работать с Ruby on Rails не разобравшись с самим языком Ruby. Ну как так можно? А?! Если лень читать книгу на 600 страниц — есть статьи на RubyDev, они не полностью корректные так как я писал их достаточно давно, но в общем достаточно годные, как мне кажется. Если нет желания прочитать краткие статьи, которые я пытался написать более чем понятным новичку языком, то, я думаю, что программирование — это не ваше. Ну да ладно. Напишу отдельную статью о self, хотя то, что такое self много где упоминалось в статьях на RubyDev.

self — это ссылка на текущий объект.

У каждого метода должен быть приемник — на то он и метод, а не функция, а если приемник не указан, то подразумевается, что приемником является self.

class A
  def hello_and_bye
    hello
    self.bye
  end

  def hello
    puts "Hello!"
  end

  def bye
    puts "Bye!"
  end
end

A.new.hello_and_bye
# Hello!
# Bye!

Разницы нет, использовали мы self или нет, но оба метода #hello и #bye были успешно выполнены.

А вот где есть разница:

class B
  def id_and_class
    puts "id: #{object_id} | class: #{self.class}"
  end
end

B.new.id_and_class
# id: 73604460 | class: B

У объектов имеется метод #class, который возвращает клас, экземпляром которого является объект, однако, существует также зарезервированное слово class, которое используется для объявления класса и вот, чтобы не было многозначности, class — это всегда объявление класса, а потому метод #class всегда следует вызывать указывая объект — приемник явно, то есть: self.class.

self — ссылка на экземпляр класса в методах экземпляра класса, так же как переменные объекта являются переменными экземпляра класса только будучи объявлены в методах экземпляра класса. В контексте тела класса (методов класса и вне их) self является ссылкой на сам класс.

class C
  def self.self
    self
  end
end

C.self #=> C

Этот страшный код, на самом деле объявляет метод .self для объекта self (класса С), который просто возвращает self (класс С). Таким образом Класс C через метод .self возвращает сам себя. Чудно, да?

self используется в трех случаях:

* Для определения методов класса

# так:
class C
  def self.self
    self
  end
end

# или так:
class C
  class << self
    def self
      self
    end
  end
end

* Когда нам нужно явно указать приемник для вызова метода

class D
  def hello
    puts "Hello!"
    bye = "Bye!"
    puts bye
  end

  def bye
    "Trololo"
  end
end

D.new.hello
# Hello!
# Bye!

class E
  def hello
    puts "Hello!"
    bye = "Bye!"
    puts self.bye
  end

  def bye
    "Trololo!"
  end
end

E.new.hello
# Hello!
# Trololo!

* Когда в качестве значения в метод или блок кода объект должен передать сам себя

def puts_id_of_obj(obj)
  puts "Id of object is: #{obj.object_id}"
end

class F
  def puts_my_id
    puts_id_of_obj self
  end
end

F.new.puts_my_id
# Id of object is: 74423470

Может есть еще какие-то экзотические случаи, но на ум ничего не пришло. Если знаете — пишите в комментариях.

Не начинайте работать с Rails, не поняв работы с Ruby. Понимание self — это самые азы, как же можно работать с Rails не зная метапрограммирования, которым пропитан фреймворк насквозь? А я уверен, что вы не знаете метапрограммирования, потому, что это сильно более сложная тема, чем self. this в JavaScript несколько сложнее self, и я уверен, что не понимая self в Ruby вы не понимаете и this в JavaScript, и наоборот. Вы не знаете ни Ruby, ни Javascript, а пытаетесь использовать один из самых современных и full-featured веб-фреймворков?! А атомную станцию не зная элементарной физики вы сможе построить? А маленькие дети могут ходить по канату не умея даже стоять?

Tags: , , ,

Responses

  1. Михаил says:

    сентября 8, 2012 at 16:27 (#)

    4 случай — для вызова метода-райтера

    self.foo = 5 # вызывает метод foo=(5)

    foo = 5 # инициализирует локальную переменную foo

  2. admin says:

    сентября 8, 2012 at 23:50 (#)

    Михаил, спасибо, но мне кажется, что это ко второму варианту относится. А вообще я стараюсь внутри объекта не использовать его аксессоры, мне так код более читабельным кажется, когда вижу @ и еще это немного производительность улучшает, но второе — это приятный бонус =)

  3. Михаил says:

    сентября 9, 2012 at 14:12 (#)

    Владимир, райтер не обязательно выступает акцессором для переменной экземпляра. Например, при назначении атрибутов в Active Record.

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

    В любом случае, можно зафигачить send безо всяких self =)

  4. arni says:

    сентября 15, 2012 at 19:19 (#)

    Возможно стоит упомянуть, что private-методы не могут вызываться от имени self: ограничение языка, видимо чтобы подчеркнуть их «приватность» :)

  5. Name says:

    августа 7, 2014 at 14:08 (#)

    Автор, да вы первостатейное хамло.
    Ничего, что ваш пост в первой пятерке поиска гугла по запросу «ruby self»?
    А вы так читающего помоями обливаете. Откуда вам, черт возьми, знать, использую я рельсы или нет?

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.