之前对 class_eval 和 instance_eval 的理解不够。在查看了相关资料后,才明白,原来在代码中,随时有这三个隐形引用:self、default_definee、cref。 其中:
对于 receiver,大家应该都清楚。 而对于 default_definee,在 class_eval 中,则还是 receiver;在 instance_eval 中,default_definee 则变成了 receiver 所指向的 eigenclass。
因此下面的代码也就不难理解了:
class A; end
A.class_eval do
def a
p :a
end
def self.sa
def b
p :b
end
p :sa
end
end
A.instance_eval do
def c
p :c
end
def self.sc
def d
p :d
end
p :sc
end
end
执行下面的语句,将输出:
A.instance_methods false # => [:a]
A.sa => :sa
A.instance_methods false # => [:a, :b]
A.c => :c
A.sc => :sc
A.instance_methods false # => [:a, :b]
A.d => :d
谢谢楼主热心讲解。我也一直觉得应该有个类似的隐形引用的。
不过我要指出楼主代码的几个问题:
A.instance_eval do
...
end
上面这个例子也不妥,会让人误解,因为 instance_eval 应该总是在receiver 作为一个对象而存在的情况下, 被使用 类似楼主的这种伪码几乎在任何情况下都不会真正发生的。换个说法,如果你想定义一个类的单例方法,根本不该这么用。而且,会增加怎么def c
和def self sc
, 定义成一样的方法的烦恼。
嗯,事实上,我觉得在 Ruby1.9 下面,唯一的可能用到类.instance_exec
的情况是:
C.instance_exec(:m) {define_method(:m) {...}} # => 动态创建一个实例方法. 即常见的attr_accesor魔法.
以前 1.8 可能还会用来动态创建类方法。不过 1.9 专门提供了 define_singleton_method 来做这个事情。
#7 楼 @imsoz
谈不上请教,客气啦.
我一般就是看 Ruby 自带的 ri 文档或者翻镐头书,另外,我会记笔记把类似的方法归纳一下。加深理解.
官方最新的文档,可以在这里查询:http://ruby-doc.org/core-1.9.2/
不过这里貌似没有 define_method 的说明。呵呵。
#7 楼 @imsoz 可以用 https://github.com/voloko/sdoc/ 来自己生成文档,里面有选项 --all,可以把 private 方法也生成到 doc 里。
Ruby ri 文档里有 define_method 啊。一般元编程时才会用的。 ruby 中,不是 private 随时可以使用,是混入到 Kernel 模块的私有方法可以随意使用. 像 define_method 只能在类定义内使用。或者其他 self 必须是一个类的环境,例如 instance_eval
类似于混入 Ruby Kernel 模块或者 Ruby Module 模块的私有方法,主要目的我个人的感觉是以下原因:
也许我没明白你的意思。不过我觉得是不是你对 Ruby 的 private 这个关键字还没理解呀。private 和其他语言的使用方式不完全一样的。
嗨~
我没用过 java, 之前N年前用过C++, 不过这么多年没用,所以作为我来说,一个好处就是:不会按照其他语言的思路来想 Ruby. 我觉得这是你必须先过的一关吧。不要总和 Java 这样的静态语言比较,Ruby 就是 Ruby. 他和之前的大多数语言都很不一样。
有关 private, 其实我不明白你为什么老关注什么是否更改啊,兼容之类的。
如果是在你自己的类中使用 private, 别人根本不会知道,也不会被调用。 但是如果在一个 DSL 中 (例如你编写了一个框架), 那么 private 就代表这是一个全局函数。他作为 DSL 的一部分,会给定义特殊的东西,带来便利。
其实我也没想透。糊里糊涂的,但是我能感觉到你现在有个问题,就是你可能并没有意识到,在 Ruby 中,一切都是对象这个价值观,其实在 Ruby 中,任何东西, 都是在 self 之上被调用的。包括顶级空间。
在 Ruby 中,一切都是对象这个价值观,其实在 Ruby 中,任何东西,都是在 self 之上被调用的。包括顶级空间。
这个应该是没有问题的,顶级其实就是一个 main 对象,包括直接输入 p :a,其实 p 就是一个 Kernel 的 private 方法,只不过没有显示指定 receiver,因此可以调用,interpreter 会沿着 main 的 class ref 找到 Object,再找到 Kernel。
我想表达的意思是,似乎 ruby 中对面向对象的封装性,在一个 javaer 看来,也不是很“封装”。比如在 instance_eval(class_eval)或者 Kernel#send 时。 我想这会带来便利,但在带来便利的同时,也需要考虑到之后的可维护性。这是我的理解。
我想 javaer 刚开始写 ruby,或多或少都会碰到传说中的语言绑架思想的状况吧。
如果是在你自己的类中使用 private, 别人根本不会知道,也不会被调用。 但是如果在一个 DSL 中 (例如你编写了一个框架), 那么 private 就代表这是一个全局函数。他作为 DSL 的一部分,会给定义特殊的东西,带来便利。
这个貌似有点想通了,比如打开 Kernel,并添加 private 方法,那么其实就和 puts 类似了:)。 从这个方面来看,这个时候,private 其实就不是传统意义上(或者说是 java 中)的 private 了。这个时候 private 只是保证了不能显示指定 receiver 调用。谢谢:)
在 ruby 中方法调用形式分为
对于 FCALL 和 VCALL,ruby 并不校验该方法是否 private,这也正是实现了那种调用全局函数的效果,比如:
def global_func
puts 'global_func'
end
class A
def method
global_func # 如果使用self.global_func, 则提示global_func为private
end
end
a = A.new
a.method
#19 楼 @imsoz 你可以参考 Ruby 的 C 源码。
但是个人觉得:用好 Ruby 的关键不在于看 Ruby 的 C 实现。虽然可以帮助你从另一角度来理解 Ruby,但是真正的用好 Ruby,写好 Ruby 代码,真心的不用看 Ruby 的 C 实现。举个例子:DHH 和 Yehuda Katz 两个 Ruby 牛就没有 C 语言的背景,这个是可以确定的,但是有谁敢说对 Rails 的理解有 DHH 深呢?
注意你是学 Ruby 而不是学 C。如果你想学动态语言的设计,或是 GC 的实现等,Ruby 和 Lua 的代码都是很好的素材。如果你想写 Ruby 的扩展,那代码也是需要看的。如果你是学Ruby 语言,那 ruby 是你学习的重点。我不反对看 Ruby 的 C 源码,但是最好还是在掌握好 Ruby 的基础之后。
换句话说,Ruby 就是 Ruby,CRuby 只是一种实现而已,其他语言的实现 JRuby 于 IronRuby 于 CRuby 的实现就差异巨大。所以 Ruby 语言本身才是学习的重点。
咱们社区里的 Rails 牛们也不见得都有 C 背景,C 背景和你能否称为 Ruby 牛或是 Rails 牛没有一点关系。罗嗦这么多~ 希望能帮到你少走弯路。
#21 楼 @imsoz 客气了。如果想了解 Ruby 的设计,而且你又熟悉 Java,完全可以看 JRuby 的代码来开阔视野,犯不着看你不熟悉的 CRuby。
就学好 Ruby(不包括 Rails) 来说,我觉得双飞燕和搞头书,再加上 api doc 基本就可以了。“要理解一样东西,就必须往下一层去理解这东西”这个道理我赞同,只是要往下探索到哪一层呢?个人认为目前可以到 Ruby 语言层即可,因为再往下就不属于 Ruby 范畴,而是属于语言设计范畴了。后期可以根据需要可以深入到 C 层。
举例来说的话,学好 C/C++ 不一定要去读懂 gcc 的所有代码。学好 java 不一定非要去读 HotSpot 的代码。作为提升功力,在掌握好语言本身之后,再进入下一个阶段比较好~。不然,很有可能事倍功半阿。
@imsoz, 不客气。
CALL、FCALL、VCALL 这几个,是属于 ruby 内部实现机制,
ruby1.9.3 p0 compile.c:4050
case NODE_CALL:
case NODE_FCALL:
case NODE_VCALL:{ /* VCALL: variable or call */
/*
call: obj.method(...)
fcall: func(...)
vcall: func
*/
这个纯属个人爱好,因为看到过,所以这里说上几句,不好意思,希望不会误导到你。
因为对于 private 为什么会是这样的设计,我也疑惑过,所以这里在多说一下, 权当做参考。
在全局作用域(也就是一般说的 toplevel)中定义的方法,在某个类中可以直接调用, 类似其他的语言中的全局函数的效果,原因就是初始脚本运行时,ruby vm 默认打开了 Object 类,并且设置方法的默认可见性为 private, 则在 toplevel 中定义的方法都是 Object 的方法且是 private 的, 由于 ruby 中的一切都是从 Object 继承的, 故 ruby 对于 private 方法的这一设计,使得在任何类的方法中, 都可以用这种类似全局函数调用的形式来进行调用。
另外附带说一句, kernel 中的 load 函数 load( file_name, wrap=false )
如果 wrap 设为 true, 就是使得 ruby vm 默认不是用 Object,而是创建一个匿名的 module, 目的是不让它污染 toplevel 的名字空间。
另外同意@skandhas 的说法,人生苦短,ruby 的宗旨就是让程序员快乐的编程。
作为提升功力,在掌握好语言本身之后,再进入下一个阶段比较好~。不然,很有可能事倍功半阿。
这个表示赞同。 就像学框架,还不会使用框架,就去看框架的源码,是最容易事半功倍的。
没注意又翻到了这个帖子,补充一下:class_eval 和 instance_eval 的一个重要区别是前者的 current class 是 receiver,而后者的 current class 是 receiver 的 eigen class