ruby如何分辨实例方法和类方法定义之间的区别?

时间:2019-01-08 02:48:17

标签: ruby

在正常的红宝石代码中,我通常可以省略self

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

但是这两个为何不同:

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

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

为什么我不能只省略self?红宝石代码编译器的观点有何不同?

我知道#1是类方法定义(Singleton方法定义),而#2是实例方法定义。但是,ruby如何与上下文和自身协同工作以实现方法定义上的这种差异?

6 个答案:

答案 0 :(得分:4)

方法正文中,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.greetingdef 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

定义实例方法时,没有显式的方式来表示“类的任何实例”,因此实际上是必须的。

答案 1 :(得分:3)

有时self是必需的(否则它将根本不存在)。您给出的第一个示例是多余的情况。这些方法调用都引用相同的方法。

有时需要 来区分两种不同的行为。

在示例1和2中,self用于确保方法是在类上定义的,而不是在类的实例上定义的。 self在这种情况下不是多余的。这是Ruby解释器知道要在何处定义方法的方式。将第一个示例中的self与示例#1和#2中的self不同可能会有所帮助。将#1和#2中的self视为“ 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

答案 2 :(得分:1)

在类上定义的方法是实例方法

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)

要定义一个接收者是一个类的方法(一个 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的值也更改为该类。 / p>

那么这与问题(即定义方法def self.my_method ...)有什么关系?简短的答案是,无需以这种方式定义方法。请耐心等待,我会解决的。

请注意,在pig的单例类中定义的方法AnimalAnimal的子类的单例类继承:

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对象执行此操作。它包含除具有立即值(按值传递的对象)之外的所有对象,这些对象包括niltruefalse,整数,符号和一些浮点数。此外,已冻结的对象(例如"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的单例类中定义方法的简便方法。仅将其视为syntactic sugar。就像在方法定义的第一行中编写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只是一种方便。

答案 3 :(得分:0)

在类中,如果您在self上定义方法,则它在类上定义它。例如

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

然后您可以致电

User.greeting
# => "Hello User"

如果定义不带self的方法,则为该类的对象定义该方法。例如:

class User
  def greeting
    "Hello user"
  end
end

然后,您必须创建一个新的用户对象来调用greeting

user = User.new
user.greeting
# => "Hello user"

答案 4 :(得分:0)

self在定义类方法时是必需的,而没有self关键字的它将被视为实例方法。 class << self; end;块还用于定义许多类方法。

这些方法是在加载类时加载的,上面的方法将有助于区分它们。

就像与现实生活中的例子相关。

您的计算机上有许多提供身份验证的帐户,具有正确密码的有效人员可以输入内部帐户。输入内部帐户后,您无需提及自己的身份!它总是会算您进入的人。

答案 5 :(得分:0)

首先,重要的是要了解在Ruby中没有 class方法 singleton方法之类的东西。只有一种方法:实例方法。

real 问题是:方法定义在哪个 module 中?

[““ Singleton方法”和“类方法”只是我们用于“类为{{1的对象的Singleton类中定义的方法”和“ Singleton类中定义的方法”的简写名称。 }}“。]

Ruby中有两种形式的方法定义:

Class

def some_expression.some_name; end

第一个将首先计算表达式def some_name; end ,然后在结果对象的 singleton类中定义一个名为some_expression的方法。

第二个将在称为默认定义的方法中定义名为some_name的方法。默认定义是通常 最近的词法包围模块定义。例如,在这种情况下:

some_name

在这里,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的实例方法,即使从技术上来说这不是模块定义,但它“只是”一个块。但是,这就是我在上面“通常”编写的原因:某些方法可以更改默认的definee,而Foo是这些方法之一(类似于Class::new更改Object#instance_eval的值的方式) )。

另一个例子:

self

在这种情况下,没有用词法包围的模块定义。在顶层,默认定义是def foo; end ,但有一点不同:默认可见性也是Object

所有这些都在yugui精彩的博客文章Three implicit contexts in Ruby中进行了详细解释。

现在,唯一缺少的是:表达式private在模块定义中的计算结果是什么?好吧,您可以根据需要对其进行测试:

self

答案是,在模块定义中,class Foo p self end 会求值为正在定义的模块。 (有点道理,对吗?)

因此,在

self

class Foo def self.foo; end end foo的单例类中定义。