`class` 定义范围内的局部变量与 `def` 方法范围
Local variables in `class` definition scope versus `def` method scope
在这里,我在 class 范围内创建了一个局部变量:
class MyClass
x = 1
puts x
end
即使我没有创建 MyClass
的任何实例,它也会打印 1
。
我想在某些方法中使用 x
:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
我不能。为什么?我知道 class 定义创建了一个范围,但为什么在方法中无法访问它?方法的范围不在 class 的范围内吗?
我可以想象这与创建 class 有关。由于任何class都是Class
的对象,所以MyClass
的范围可能是某些Class
方法的范围,以及MyClass
的耦合方法的方式那个实例使它们的范围完全不同。
在我看来,我不能只用 {}
(就像在 C 中一样)或 do..end
之类的东西创建一个范围。我说得对吗?
方法的作用域是而不是 class。每个方法都有自己全新的作用域。
只要您使用 class
、module
和 def
关键字,就会创建新范围。与在 C 中一样,使用方括号不会创建新的作用域,事实上您不能使用方括号任意组合代码行。 Ruby 块周围的方括号(或 do
...end
)创建块级作用域,其中先前在周围作用域中创建的变量可用,但在块内创建的变量作用域之后不会逃逸到周围的作用域中。
实例方法与其他实例方法共享它们的实例变量的范围。在 class 定义范围内定义的实例变量在 class 级别的单例方法中可用,但在 class.
的实例方法中不可用
插图:
class Foo
x = 1 # available only here
@y = 2 # class-wide value
def self.class_x
@x # never set; nil value
end
def self.class_y
@y # class-wide value
end
def initialize(z)
x = 3 # available only here
@z = z # value for this instance only
end
def instance_x
@x # never set; nil
end
def instance_y
@y # never set; nil
end
def instance_z
@z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
您可以使用 class 级 getter 从实例中访问 class 级实例变量。继续上面的例子:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
Ruby 中存在使用 @@
印记的 "class variable," 概念。实际上,这种语言结构几乎没有合理的用例。通常,使用 class 级实例变量可以更好地实现目标,如此处所示。
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1
even if I don't create any instances of MyClass
.
正确。 class 定义体在读取时执行。它只是像任何其他代码一样的代码,class 定义体没有什么特别之处。
问问自己:attr_reader
/attr_writer
/attr_accessor
、alias_method
、public
/protected
/private
否则工作?哎呀,如果 def
在定义 class 时没有被执行,它会如何工作? (毕竟,def
和其他表达式一样只是一个表达式!)
这就是为什么你可以做这样的事情:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use x
in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
不,不是。 Ruby中有4个作用域:脚本作用域、module/class定义作用域、方法定义作用域和block/lambda作用域。只有 blocks/lambdas 嵌套,所有其他的都创建新的范围。
I can imagine that this is related to creation of a class. Since any class is an object of Class
, maybe the scope of MyClass
is the scope of some Class
method, and the way of coupling methods of MyClass
to that instance makes their scope completely different.
老实说,我不完全明白你在说什么,但是不,class定义范围不是方法定义范围,class定义范围是class定义范围,和方法定义范围是方法定义范围。
It also seems to me that I can't just create a scope with {}
(like in C) or something like do..end
. Am I correct?
就像我上面说的:Ruby 中有 4 个作用域。 C 中没有块作用域。("block" 的 Ruby 概念与 "block." 的 C 概念完全不同)你能得到的最接近的东西是 JavaScript-inspired immediately-invoked lambda-literal,像这样:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
一般来说,在Ruby中没有必要。在结构良好的代码中,方法会非常小,跟踪局部变量及其作用域和生命周期真的不是什么大问题。
So just creating a class without any instances will lead to something
actually executing in runtime (even allocating may be)? That is very
not like C++. –
查看此代码:
Dog = Class.new do
attr_accessor :name
def initialize(name)
@name = name
end
end
如果你执行那段代码,不会有任何输出,但还是会发生一些事情。例如,创建了一个名为 Dog 的全局变量,它有一个值。证明如下:
Dog = Class.new do
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
上面对Dog常量的赋值相当于这样写:
class Dog
...
...
end
而且,事实上,ruby 遍历 class 定义中的每一行并执行每一行——除非代码行在 def 中。 def 已创建,但 def 中的代码在调用 def 之前不会执行。
您将在 class 定义中看到的一个非常常见的行是:
attr_accessor :name
...可以重写为:
attr_accessor(:name)
...这很明显这是一个方法调用。当您 运行 包含 class 定义的文件时,Ruby 执行该行并调用该方法。 attr_accessor
方法然后 动态创建并插入 一个 getter 和一个 setter 方法到 class 中。在运行时。是的,这不再是 C++ 领域了——欢迎来到 NeverNever Land。
I get that class definition creates a scope, but why is it not
accessible in the method?
因为那是 Matz 决定事情应该的方式:def
创建一个新的作用域,阻止变量在 def 之外的可见性。但是,有一些方法可以打开作用域门,可以说:blocks
可以看到周围作用域中定义的变量。查看 define_method()
:
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
块是 do...end
之间的所有内容。在ruby中,一个块是一个闭包,这意味着当一个块被创建时,它会捕获周围范围内的变量,并携带这些变量,直到块被执行。块就像一个匿名函数,它作为参数传递给方法。
请注意,如果您使用 Class.new 技巧,您可以打开两个范围门:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
一般来说,ruby 允许程序员做任何他们想做的事,规则是该死的。
在这里,我在 class 范围内创建了一个局部变量:
class MyClass
x = 1
puts x
end
即使我没有创建 MyClass
的任何实例,它也会打印 1
。
我想在某些方法中使用 x
:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
我不能。为什么?我知道 class 定义创建了一个范围,但为什么在方法中无法访问它?方法的范围不在 class 的范围内吗?
我可以想象这与创建 class 有关。由于任何class都是Class
的对象,所以MyClass
的范围可能是某些Class
方法的范围,以及MyClass
的耦合方法的方式那个实例使它们的范围完全不同。
在我看来,我不能只用 {}
(就像在 C 中一样)或 do..end
之类的东西创建一个范围。我说得对吗?
方法的作用域是而不是 class。每个方法都有自己全新的作用域。
只要您使用 class
、module
和 def
关键字,就会创建新范围。与在 C 中一样,使用方括号不会创建新的作用域,事实上您不能使用方括号任意组合代码行。 Ruby 块周围的方括号(或 do
...end
)创建块级作用域,其中先前在周围作用域中创建的变量可用,但在块内创建的变量作用域之后不会逃逸到周围的作用域中。
实例方法与其他实例方法共享它们的实例变量的范围。在 class 定义范围内定义的实例变量在 class 级别的单例方法中可用,但在 class.
的实例方法中不可用插图:
class Foo
x = 1 # available only here
@y = 2 # class-wide value
def self.class_x
@x # never set; nil value
end
def self.class_y
@y # class-wide value
end
def initialize(z)
x = 3 # available only here
@z = z # value for this instance only
end
def instance_x
@x # never set; nil
end
def instance_y
@y # never set; nil
end
def instance_z
@z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
您可以使用 class 级 getter 从实例中访问 class 级实例变量。继续上面的例子:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
Ruby 中存在使用 @@
印记的 "class variable," 概念。实际上,这种语言结构几乎没有合理的用例。通常,使用 class 级实例变量可以更好地实现目标,如此处所示。
Here, I create a local variable in class scope:
class MyClass x = 1 puts x end
It prints
1
even if I don't create any instances ofMyClass
.
正确。 class 定义体在读取时执行。它只是像任何其他代码一样的代码,class 定义体没有什么特别之处。
问问自己:attr_reader
/attr_writer
/attr_accessor
、alias_method
、public
/protected
/private
否则工作?哎呀,如果 def
在定义 class 时没有被执行,它会如何工作? (毕竟,def
和其他表达式一样只是一个表达式!)
这就是为什么你可以做这样的事情:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use
x
in some method:class MyClass x = 1 def method puts x end end m = MyClass.new m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
不,不是。 Ruby中有4个作用域:脚本作用域、module/class定义作用域、方法定义作用域和block/lambda作用域。只有 blocks/lambdas 嵌套,所有其他的都创建新的范围。
I can imagine that this is related to creation of a class. Since any class is an object of
Class
, maybe the scope ofMyClass
is the scope of someClass
method, and the way of coupling methods ofMyClass
to that instance makes their scope completely different.
老实说,我不完全明白你在说什么,但是不,class定义范围不是方法定义范围,class定义范围是class定义范围,和方法定义范围是方法定义范围。
It also seems to me that I can't just create a scope with
{}
(like in C) or something likedo..end
. Am I correct?
就像我上面说的:Ruby 中有 4 个作用域。 C 中没有块作用域。("block" 的 Ruby 概念与 "block." 的 C 概念完全不同)你能得到的最接近的东西是 JavaScript-inspired immediately-invoked lambda-literal,像这样:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
一般来说,在Ruby中没有必要。在结构良好的代码中,方法会非常小,跟踪局部变量及其作用域和生命周期真的不是什么大问题。
So just creating a class without any instances will lead to something actually executing in runtime (even allocating may be)? That is very not like C++. –
查看此代码:
Dog = Class.new do
attr_accessor :name
def initialize(name)
@name = name
end
end
如果你执行那段代码,不会有任何输出,但还是会发生一些事情。例如,创建了一个名为 Dog 的全局变量,它有一个值。证明如下:
Dog = Class.new do
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
上面对Dog常量的赋值相当于这样写:
class Dog
...
...
end
而且,事实上,ruby 遍历 class 定义中的每一行并执行每一行——除非代码行在 def 中。 def 已创建,但 def 中的代码在调用 def 之前不会执行。
您将在 class 定义中看到的一个非常常见的行是:
attr_accessor :name
...可以重写为:
attr_accessor(:name)
...这很明显这是一个方法调用。当您 运行 包含 class 定义的文件时,Ruby 执行该行并调用该方法。 attr_accessor
方法然后 动态创建并插入 一个 getter 和一个 setter 方法到 class 中。在运行时。是的,这不再是 C++ 领域了——欢迎来到 NeverNever Land。
I get that class definition creates a scope, but why is it not accessible in the method?
因为那是 Matz 决定事情应该的方式:def
创建一个新的作用域,阻止变量在 def 之外的可见性。但是,有一些方法可以打开作用域门,可以说:blocks
可以看到周围作用域中定义的变量。查看 define_method()
:
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
块是 do...end
之间的所有内容。在ruby中,一个块是一个闭包,这意味着当一个块被创建时,它会捕获周围范围内的变量,并携带这些变量,直到块被执行。块就像一个匿名函数,它作为参数传递给方法。
请注意,如果您使用 Class.new 技巧,您可以打开两个范围门:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
一般来说,ruby 允许程序员做任何他们想做的事,规则是该死的。