ruby的instance_eval和class_eval, self && current class

        转载请包含源链接:http://blog.csdn.net/wu4long/article/details/42191929

        ruby语言中,尤其是meta programming中,很多初学者或者有一定经验者,都未必能搞的清楚instance_eval和class_eval以及self和current class。

        首先我们要明确一点,在任何时刻,ruby都会一直追踪当前的对象(self)和当前的类(current class)。只有把握这两个东西,所有的元编程的手法都可以理解它里面的含义。

        在ruby metaprogramming一书中,是这样来描述的instance_eval和class_eval:这两个method都会更改当前的对象(self)和当前的类(current class)。obj.instance_eval 会将当前的self 改为obj, current class为obj的eigenclass(或者说singleton class,以#obj来表示obj的eigenclass)。 而 klass.class_eval 会将当前的self改为klass, current class为 klass。

       从理论来来说,上面解释的是很正确的也很到位的。但为什么很多人,就算阅读了上面的说明,一时半会也无法真正理解一些实际的代码(包括我本人在内)。我感觉是没有人真正把self和current class的一些变化的情况给一个完整的总结,只有完整的总结出来,初学者才能有纲可循。

        self和current class的变更情况:

        1. module 或者 class 定义中, self 和 current class 都会变更为当前的module。

              class A

                    # self is A, current class is A

              end

        2.  klass.class_eval 也是将self和current class都会变更成当前的klass.

             A.class_eval do

                    # self is A, current class is A.

             end     

        3. 显式打开eigenclass, 此时会将当前的#obj(obj的eigenclass)变更为self, #obj(obj的eigenclass)为current class.

             class << A

                 # self is #A,   current class is #A.

             end

        4. obj.instance_eval 也会将当前的obj变更为self, #obj(obj的eigenclass)为current class.

              A.instance_eval do

                  # self is A, current class is #A                          

              end

      基本上搞清楚了上面的这4条,很多代码基本都知道它的所以然了。

       5. def obj.singleton_method 此时self和current class 会变更吗?

          从def这个关键字来看,它只是在当前的current class定义实例方法。不存在执行代码,因此self不会变更的,current class也不会变更的。 如果加上obj.之后,#obj会变更当前的current class?如果只是定义一个方法而言,从结果来看,确实是在#obj增加了实例方法,那此时current class到底变了没? 我们暂时不回答,你可以假设它变了。

       6. obj.method 此时会把self变更为obj, 但current class不会变更。

       

      7. 在方法中method_a 再通过def  来定义新的方法 method_a_inner,此时method_a_inner会将定义method_a时的current class进行bind,而不是和调用方法method_a此时的current class相关。

        我们通过下面的情况来分析。

           1) 

             class A

                   def self.singleton_a

                         def h_a

                                "h_a self is #{self}"

                         end

                   end 

             end

            2)

              class A

                   class << A

                         def singleton_a

                               def h_a

                                     "h_a self is #{self}"

                               end

                         end

                   end

              end

           3)

               class A

               end

                def A.singleton_a

                             def h_a

                                     "h_a self is #{self}"

                             end

                end

          上面三种情况,都是在A的eigenclass(#A)定义了方法singleton_a。 如果我们调用 A.singleton_a 之后,方法 h_a 究竟是 A的实例方法还是 #A(A的eigenclass)的实例方法呢?

           A.singleton_a

           A.respond_to?(:h_a)   #  true or false    ????????????????

           A.new.respond_to?(:h_a) #  true or false ??????????????????

           Object.new.respond_to?(:h_a) #true or false ????????????

       1)  第一种情况:

               A.singleton_a

               A.respond_to?(:h_a)           # false
               A.new.respond_to?(:h_a)   # true

               Object.new.respond_to?(:h_a) # false

             2)第二种情况:

               A.singleton_a

               A.respond_to?(:h_a)           # true
               A.new.respond_to?(:h_a)   # false

              Object.new.respond_to?(:h_a) # false

              3) 第三种情况              

               A.singleton_a

               A.respond_to?(:h_a)           # true
               A.new.respond_to?(:h_a)   # true

               Object.new.respond_to?(:h_a) # true

         同样调用的是A.singleton_a的方法,为何三种情况的结果都是截然不同呢?

         从这里看出,obj.method 时,是不会变更current class的,如果变更的话,那同样的对象,调用同样的方法应该current class是一致的。那为何当前的current class有差异呢?估计和语言本身设计相关。def 只和词法相关,由当前的定义时的current class进行绑定,而和方法调用时的current class无关。

         第一种情况,current class为A,第二种情况 current class 为#A, 第三种情况current class为Object。

         由上面的结论,我们知道了,第一种情况下 def A.singleton_a 的current class为A,第三种情况下 current class为Object,从而我们可以反向推断,上面第5条提出的问题,def A.singleton_a 不会变更current class,只是语言自身强行将此方法放入#A中。而在此方法中再定义方法时,还是把当前的current class也bind到一起了。从而我们才能解释上面的情况。

        至此,我们基本都解释了current class 和 self一些变更的情况。

分析一个简单而又容易被搞混淆的实例,

A.instance_eval do

   define_method(:t1) do

       puts "t1"

       def t4

       end

   end

   def t2

     "t2"

      def t3

      end

   end

end

根据上面的说法,A.instance_eval 将当前的self变更为A,current class为#A(A的eigenclass)。

我们说过,def 是根据当前的词法分析上的current class,在这边来说,就是#A,因此 t2 是#A的实例方法。而define_method是调用于self上的,也就是A,所以A有个实例方法 t1.

即:A.respond_to?(:t2) # true

       A.new.respond_to?(:t1) # true

调用 A.t2, A.new.t1之后, t3 和 t4 分别是那个class的实例方法呢?从我们刚才说的,在定义def t3,t4时,词法分析当前的current class是#A,从而t3和t4也就是#A的实例方法。而测试的结果也证实了这一点。

      A.respond_to?(:t3)  #true

      A.respond_to?(:t4)  #true

      A.new.respond_to?(:t3) # false     

      A.new.respond_to?(:t4) # false

至此我们基本上分析完了,self和current class的变更情况。并且也把def  和 define_method的进行了区分。define_method是在self(这个class)上添加的一个实例方法,而def是在词法分析中的current class类中定义的一个实例方法。 而有些时候,self和current class都是一样的,比如: class A; end  和 A.class_eval 中,此时 define_method和def就产生一样的效果了,从而有些书或者资料就说,这两者是等同的,或者说define_method只是破除了scope的限制,可以引用当前的局部变量了。但真正他们的区别就没人来说的透彻,从而大家都是一知半解。

       当然,估计我有些地方说的不准确,不全面,也希望大家指正。只是按照我目前的说法,我能把我现在遇到的问题都可以迎刃而解。如果后面遇到了不能解释的,我们再进行具体分析和更正。

   

你可能感兴趣的:(Singleton,Ruby,Metaprogramming)