Как ruby ​​показывает разницу между методом экземпляра и определением метода класса? - PullRequest
0 голосов
/ 08 января 2019

В нормальном рубиновом коде я обычно могу пропустить self:

class User
  def greeting
    # these 2 are the same:
    puts "Hi, #{name}"
    puts "Hi, #{self.name}"
  end
end

но почему эти 2 разные:

# 1
class User
  def self.greeting
    # ...
  end
end

# 2
class User
  def greeting
    # ...
  end
end

Почему я не могу просто опустить self? В чем разница с точки зрения компилятора ruby-кода?

Я знаю, что # 1 - определение метода класса (определение метода Singleton), а # 2 - определение метода экземпляра. Но как ruby ​​работал с контекстом и self для достижения этой разницы в определении метода?

Ответы [ 6 ]

0 голосов
/ 08 января 2019

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

Вопрос real таков: какой модуль является методом, определенным в?

[«Метод Singleton» и «метод класса» - это просто сокращенные имена, которые мы используем для «метода, определенного в классе singleton» и «метода, определенного в классе singleton объекта, класс которого Class ».]

В Ruby есть две формы определений методов:

def some_expression.some_name; end

и

def some_name; end

Первый из них сначала оценит выражение some_expression, а затем определит метод с именем some_name внутри одноэлементного класса результирующего объекта.

Второй будет определять метод с именем some_name в том, что называется defaulte defaulte . Определением по умолчанию является , обычно , ближайшее определение лексического модуля . Например, в этом случае:

module Foo
  module Bar
    def foo
      def bar; end
    end
  end
end

include Foo::Bar

foo
# At this point, `bar` will be defined, but where?

Здесь bar будет определен как метод экземпляра Foo::Bar, потому что bar является наиболее близким лексически ограничивающим определением модуля. Есть еще одно лексическое определение, которое еще ближе, а именно определение метода foo, но это не определение модуля.

Немного загадочнее:

Foo = Class.new do
  def foo; end
end

Это определит foo как метод экземпляра Foo, хотя технически это не определение модуля, это «просто» блок. Однако именно поэтому я написал «обычно» выше: некоторые методы могут изменять определение по умолчанию, и Class::new является одним из этих методов (аналогично тому, как, например, Object#instance_eval меняет значение self).

Другой пример:

def foo; end

В этом случае не существует определения лексически заключенного модуля. На верхнем уровне определением по умолчанию является Object, но с изюминкой: видимость по умолчанию также private.

Все это объясняется более подробно в блестящем сообщении в блоге Три неявных контекста в Ruby от yugui.

Теперь, единственное, чего не хватает: что вычисляет выражение self внутри определения модуля? Ну, вы можете проверить это, если хотите:

class Foo
  p self
end

Ответ заключается в том, что в определении модуля self соответствует определению модуля. (В смысле имеет смысл, не так ли?)

Следовательно, в

class Foo
  def self.foo; end
end

foo определено в классе синглтона Foo.

0 голосов
/ 08 января 2019

Методы, определенные в классе: методы экземпляра :

class Animal
  def dog
    "woof"
  end

  def cat
    "meow"
  end
end

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

Animal.instance_methods(false)
  #=> [:dog, :cat] 
animal = Animal.new
  #=> #<Animal:0x00005bfb0c55ae98>  
animal.dog
  #=> "woof"
Animal.dog
  #=> NoMethodError (undefined method `dog' for Animal:Class)

Чтобы определить метод, получателем которого является класс ( метод класса ), мы определяем метод в классе синглтона класса. Для класса Animal мы могли бы написать одно из следующих:

class Animal
  class << Animal
    def pig(n)
      "#{n} little pigs"
    end
  end
end

Animal.methods(false)
  #=> [:pig] 
Animal.pig(3)
  #=> "3 little pigs"

или

Animal.define_singleton_method(:pig) do |n|
  "#{n} little pigs"
end

Animal.methods(false)
  #=> [:pig] 
Animal.pig(3)
  #=> "3 little pigs"

Строка class < Animal 1 изменяет область действия на одноэлементный класс Animal, в результате чего значение self также изменяется на этот класс.

Так, как это связано с вопросом, то есть определением методов def self.my_method ...? Краткий ответ: нет необходимости определять методы таким образом. Пожалуйста, будьте терпеливы - я доберусь до этого.

Обратите внимание, что метод pig, определенный в одноэлементном классе Animal, наследуется одноэлементным классом подклассов Animal:

class Swine < Animal
end

Swine.instance_methods & [:dog, :cat]
  #=> [:dog, :cat]
Swine.methods & [:pig]
  #=> [:pig] 

Мы также можем определить методы для многих уникальных объектов . Рассмотрим animal, экземпляр Animal:

animal.define_singleton_method(:rodent) do |n|
  "I'm rodent ##{n}"
end

animal.rodent(3241)
  #=> "I'm rodent #3241"

animal - единственный получатель, на который ответит этот метод:

Animal.new.rodent(55)
  #=> #NoMethodError (undefined method `rodent' for
  #     #<Animal:0x00005bfb0c530670>)

Фактически, мы можем определить методы для каждого объекта, который имеет одноэлементный класс, который является большинством объектов:

str = "cow"
str.define_singleton_method(:greeting) { "moo" }
str.greeting
  #=> "moo"

arr = [1,2]
arr.define_singleton_method(:greeting) { "I'm an array" }
arr.greeting
  #=> "I'm an array"

module M; end
M.define_singleton_method(:greeting) { "I'm a module" }
M.greeting
  #=> "I'm a module"

piggy = Animal.method(:pig)
  #=> #<Method: Animal.pig> 
piggy.define_singleton_method(:greeting) { 
  "I'm a singleton method" }
piggy.greeting
  #=> "I'm a singleton method"

Мы можем сделать это со всеми объектами Ruby, которые имеют одноэлементный класс. Это включает в себя все объекты, кроме тех, которые имеют непосредственные значения (объекты, переданные по значению), которые включают nil, true, false, целые числа, символы и некоторые числа с плавающей точкой. Кроме того, объекты, которые были заморожены (например, "Hi".freeze), не имеют одноэлементного класса.

Предположим, теперь мы пишем

class Animal
  def Animal.pig(n)
    "#{n} little pigs"
  end
end

или (тоже самое)

class Animal
  def self.pig(n)
    "#{n} little pigs"
  end
end

(Мы наконец там!)

Что это за новый способ определения метода? На самом деле это просто краткий способ определения метода в синглтон-классе Animal. Думайте об этом как о синтаксическом сахаре . Так же, как запись 2 + 2 указывает Ruby выполнить 2.+(2), Animal. или self. в первой строке определения метода, просто указывает Ruby выполнить следующее.

class Animal
  class << self
    def pig(n)
      "#{n} little pigs"
    end
  end
end

Другими словами, создание метода класса с помощью записи def Animal.my_method... или def self.my_method... вообще не требуется; это просто для удобства Ruby-кодеров.

1 Эта строка обычно пишется class << self, что допустимо, так как self равно Animal при выполнении строки. Использование << self - просто удобство, если класс будет переименован.

0 голосов
/ 08 января 2019

self является обязательным при определении методов класса, тогда как без ключевого слова self оно будет рассматриваться как методы экземпляра. Блок class << self; end; также используется для определения номеров методов класса.

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

Это будет просто похоже на пример из реальной жизни .

У вас есть количество учетных записей с аутентификацией на вашем компьютере, действительный человек с правильным паролем может войти в учетную запись. После того, как вы вошли в личный кабинет, вам не нужно упоминать, кто вы есть! Он всегда считает тебя тем, кто вошел.

0 голосов
/ 08 января 2019

В теле метода , self относится к получателю . В строках 3..4 ниже, как только получатель определен как экземпляр User (по синтаксису def greeting), self относится к этому экземпляру.

class User
  def greeting
    puts "Hi, #{name}"      # method body
    puts "Hi, #{self.name}" # method body
  end
end

В теле класса , self относится к классу . В строках 2, 4, 8, 10 следующего класса это User, поэтому def self.greeting совпадает с def User.greeting.

class User
  def self.greeting # class body
    # ...
  end               # class body
end

class User
  def greeting      # class body
    # ...
  end               # class body
end

Но на самом деле я думаю, что ваша настоящая проблема не в том, что означает self, а в том, что означает "опущенный получатель" в разных контекстах.

В методе - , вызывающем синтаксис , опущенный получатель означает self. Итак, следующие два одинаковы:

name
self.name

В методе - , определяющем синтаксис , опущенный получатель означает «любой экземпляр класса». Таким образом, следующие два не совпадают:

def User.greeting; ... end
def greeting; ... end

Когда вы определяете метод экземпляра, не существует явного способа выразить «любой экземпляр класса», поэтому на самом деле упущение является обязательным.

0 голосов
/ 08 января 2019

Иногда требуется self (иначе его бы вообще не было). Первый пример, который вы приводите, - это случай, когда он избыточен. Оба этих вызова методов относятся к одному и тому же методу.

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

В ваших примерах № 1 и № 2 self используется для того, чтобы убедиться, что метод определен в классе, а не в экземплярах класса. В этом случае self не является избыточным. Именно так интерпретатор Ruby знает, где вы хотите определить метод. Может быть полезно думать о self в вашем самом первом примере как о чем-то отличном от self в примерах № 1 и № 2. Может быть полезно думать о self в # 1 и # 2 как "class"? Это одно и то же ключевое слово, но то, как они используются, не является взаимозаменяемым.

Еще один пример того, где он не является избыточным:

# user.rb

def method1
  name = "Henry" # Sets a local variable called `name`
end

def method2
  self.name = "Henry" # Sets the user's `name` attribute
end
0 голосов
/ 08 января 2019

В классе, если вы определяете метод для себя, он определяет его для класса. Э.Г.

class User
  def self.greeting
    "Hello User"
  end
end

Тогда вы можете позвонить

User.greeting
# => "Hello User"

Если вы определяете метод без self, то этот метод определяется для объектов этого класса. E.g.:

class User
  def greeting
    "Hello user"
  end
end

Затем вам нужно создать новый пользовательский объект для вызова greeting.

user = User.new
user.greeting
# => "Hello user"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...