ruby Metaprogramming和关键字自我混淆

时间:2015-02-28 14:02:48

标签: ruby

正在阅读红宝石中使用的一些自我代码,仍然无法弄清楚一些例子:

class A
  def hi
    "hi"
  end
  def self.hello
    "hello"
  end
end
a=A.new
a.hi
A.hello

明白我可以使用下面的方法来改变实例方法def:

class << a
    def hi
      "hi from a <<"
    end
end

a.hi => "hi from a <<"

但是这是什么?

class << a
  def self.hello
    "H E L L O"
  end
end


[21] pry(main)> A.hello
=> "hello"
[22] pry(main)> a.hello
<<ERROR1>> NoMethodError: undefined method `hello' for #<A:0x007fe41338ecb8>

我真的不确定我在这里做什么。

和定义之间有什么区别

class << a
  def hi
    "hi from a <<"
  end
end

and

class << A
  def hi
    "hi from a <<"
  end
end

----------------------问题进一步增加了-------------------- -

class A
  p "in class A: #{self}, type: #{self.class}"

  def f
    p "in f: #{self}, type: #{self.class}"
  end

  def self.m
    p "in selfm: #{self}, type: #{self.class}"
  end

end

metaclass = class << a;self;end

metaclass.instance_eval do

  "hi : #{self}, type: #{self.class}"
  def f7
    "in f7 .. : #{self}, type: #{self.class}"
  end

  def self.f9
    "in f7 .. : #{self}, type: #{self.class}"
  end

end

A.f7
a.f7
A.f9
a.f9


<<ERROR2>> 

[20] pry(main)&gt; A.f9 NoMethodError:未定义的方法f9' for A:Class from (pry):40:in pry &#39; [21] pry(主要)&gt; a.f9 NoMethodError:未定义的方法f9' for #<A:0x007fb70717c0d0> from (pry):41:in pry &#39;

<<ERROR3>> 

[22] pry(main)&gt; [23] pry(主要)&gt; A.f7 NoMethodError:未定义的方法f7' for A:Class from (pry):42:in pry &#39; [24] pry(主要)&gt; a.f7 NoMethodError:未定义的方法f7' for #<A:0x007fb70717c0d0> from (pry):43:in pry &#39; [25] pry(主要)&gt; A.f9 NoMethodError:未定义的方法f9' for A:Class from (pry):44:in pry &#39; [26] pry(主要)&gt; a.f9 NoMethodError:未定义的方法f9' for #<A:0x007fb70717c0d0> from (pry):45:in pry &#39;

您能否明确指出这些错误究竟是什么:请参阅&lt;&gt;标记

  1. 看到&lt;&gt;,我的问题:
  2. 在对象中定义self.method是否有意义,如果没有,为什么没有warring /错误?如果它有意义,它对于一个物体的自我方法意味着什么?

    1. &gt;,为什么f7不能同时使用A&C的类和对象调用?

    2. &gt;,为什么f9不能使用A&C的类和对象调用?

    3. 再讨论一下:

      &LT;&GT;

      class A
          def self.f1
            "f1"
          end
      
          def self.f2(&block)
            (class << self; self; end).instance_eval do
              define_method("f1", &block)
            end
          end
      
      
      
          def self.f3(&block)
            m=(class << self; self; end)
              m.instance_eval do
              define_method("f1", &block)
            end
          end
      
      
          def self.f4(&block)
            m=(class << self; self; end)
              m.instance_eval do
                def f1
                  "f4 is called"
                end
            end
          end
      
      
          def self.f5(&block)
            m=(class << self; self; end)
              m.instance_eval do
                def f1
                  "f5 is called"
                end
            end
          end
      
      end
      

      似乎我现在更接近真相,这里的最后一个需要揭开魔力:

      如果我这样做

      A.f2 do
        "f2 is called"
      end
      
      A.f1
      
      A.f3 do
        "f3 is called"
      end
      
      A.f1
      

      我可以用A.f2或A.f3调用覆盖f1方法,但是如果我在instance_eval块中直接使用def方法,它就不会实现相同的目标,那么差异是什么这里吗?

      A.f4
      
      A.f1
      
      A.f5
      
      A.f1
      

      A.f1仍然返回&#34; f1&#34;。我发现如果你使用def,那么方法被定义并与特征类实例绑定,如果你使用define_method,那么该方法与类A绑定为静态方法。

      define_method和def之间的区别是什么?

1 个答案:

答案 0 :(得分:3)

您在这里提出的问题实际上是关于类,方法和对象如何在Ruby中工作的基础知识。

您所做的只是class方法和instance方法之间的区别。

instance方法在最基本的意义上意味着只能从实例化对象的级别调用该方法。

这就是你在这里看到的语法。

class A
  def hi
    "hi"
  end
end

在这种情况下,方法hi是类A的实例方法,因此我们必须实例化一个对象来调用它。为避免套管A与A之间出现混淆,我将使用foo代替a

foo = A.new
foo.hi

现在我们刚刚创建了instance foo A,因此我们可以在foo上调用方法hi

接下来,我们有class个方法,或者可以在类级别调用的方法。在Ruby中,有多种语法可以实现这一点,所有以下内容在语义上都是等效的。

class A
  self.hello
    "hello"
  end
end

class A
  A.hello
    "hello"
  end
end

class A
  class << self
    def hello
      "hello"
    end
  end
end

现在使用class << self的最后一个版本的一个好处是我们可以在该部分中定义多个方法。我在上面的最后两种方法之间来回移动,取决于我正在做什么,几乎从不使用第一个版本,但这只是个人偏好。

现在,到目前为止,我所说的一切都是Ruby的标准OO类/方法定义的实现,类似的概念将在你使用C ++,Java,C#等的任何其他OO语言中找到。 ..

你感到困惑的最后一篇文章是Ruby介入元类的想法,或者它们通常在Ruby中被称为“本征类”。

这基本上意味着在类和实例之间存在一个您没有定义的类。这有点令人困惑;请阅读此处以获得更清晰(http://en.wikipedia.org/wiki/Metaclass)。

但这允许我们做的是直接在实例上定义方法,因此方法仅存在于该实例上,而不存在于该类的其他实例上。

这是下面的语法所在。

class << foo
  def hi
    "hi from foo"
  end
end 

现在,仅为实例hi覆盖了方法foo。这也可以用于定义仅存在于实例上的全新方法。例如

class << foo
  def bar
    "bar"
  end
end

现在,如果我们实例化A

的新实例
b = A.new

b上不存在方法bar;它只存在于实例foo上。