在正常的红宝石代码中,我通常可以省略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如何与上下文和自身协同工作以实现方法定义上的这种差异?
答案 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.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
定义实例方法时,没有显式的方式来表示“类的任何实例”,因此实际上是必须的。
答案 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
的单例类中定义的方法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
的单例类中定义方法的简便方法。仅将其视为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
的单例类中定义。