有关load中的参数true

再探Ruby中的load。

Ruby中的load method用来加载文件,也就是将另一个ruby文件中的代码加载进来了。

《Ruby元编程》中提到,使用load时,可能会出现常量污染当前程序的命名空间的问题,添加了true这个参数后,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量,从而避免污染的问题,但是书中并没有给出例子来详细说明。

来看两个例子感受下:

同一个目录下,存在两个文件,a.rb, b.rb,a中加载了b.rb文件。

# b.rb
## 只定义了一个普通的常量Y
Y = "hello"

a.rb中加载b.rb

# a.rb
load './b.rb'

Y = "hi"

p Y
#=> warning: already initialized constant Y

此时,执行a.rb,会报错,显示:warning: already initialized constant Y

也就是加载进来的常量Y与a.rb中的常量Y重了,系统报错显示已经初始化了Y。

如果添加了true,则输出”hi”。

# a.rb
load './b.rb', true

Y = "hi"

p Y
#=> hi

这里Y是一个常规的常量,ruby中类名,模块名也是常量,如果加载同名的类/模块会出现什么情况?

修改下b.rb文件:

# b.rb
module Mymodule
    def hi
        p "method hi in b.rb"
    end
end

修改下a.rb文件:

# a.rb
load './b.rb'

class Mymodule
    def hello
        p "hello"
    end
end

Mymodule.new.hello

#=> Mymodule is not a class

可以看到,加载后,执行a.rb时,报错,显示Mymodule is not a class,因为此时加载进来了一个同名的Mymodule,它是一个module,而a.rb文件中,定义了一个同名的Mymodule类,这个类其实并没有创建成功,这里稍微提一下,执行a.rb时,报错不是发生在执行Mymodule.new.hello时,而是在定义Mymodule这个类的地方报错。

如果添加上true,就正常了。

# a.rb
load './b.rb', true

class Mymodule
    def hello
        p "hello"
    end
end

Mymodule.new.hello

#=> hello

这里,你可能会疑惑,添加了true之后,原本来自b.rb中那个Y,module Mymodule去了哪里?书中提到,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量, 加载完成后,该模块会被销毁,也就是等同于他们根本就没有加载进来了?怎么知道它们真的没有加载进来?

可以通过Module的类方法constants来判断,该方法返回当前顶层程序中的所有常量。

修改下b.rb ,改回原来的样子:

# b.rb

Y = "hello"

修改下 a.rb:

# a.rb
# 未加载b.rb

p Module.constants

#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]

p Module.constants.count
#=> 117

p Module.constants.include? :Y
#=> false

加载b.rb,无true:

# a.rb
load './b.rb'

p Module.constants

#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :Y, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]

p Module.constants.count
#=> 118

p Module.constants.include? :Y
#=> true

此时Y加载进来了,常量总数加1。

加载b.rb,有true:

# a.rb
load './b.rb', true

p Module.constants

#=> [:Integer, :Float, :String, :Array, :Hash, :NilClass, :STDERR, :STDIN, :NIL, :Delegator, :STDOUT, :ARGF, :SimpleDelegator, :UncaughtThrowError, :FileTest, :File, :GC, :Fiber, :FiberError, :Rational, :ObjectSpace, :Gem, :DidYouMean, :Complex.......]

p Module.constants.count
#=> 117

p Module.constants.include? :Y
#=> false

输出结果与未加载b.rb完全一致。

也就是书中说的那样,添加true后,Ruby会创建一个匿名模块,用它作为命名空间来容纳加载文件中定义的所有常量, 加载完成后,该模块会被销毁

等同于load添加true后,所有的常量都没有加载进来。当然这里的常量,包括类名,模块名。

参考

Ruby元编程(第2版)