Ruby元编程与关键字自混淆

xvw2m8pv  于 2023-03-08  发布在  Ruby
关注(0)|答案(1)|浏览(133)

读了一些自己在ruby中使用的代码,仍然不能理解一些例子:

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

我明白我可以用下面的方法来改变示例方法定义:

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]查询(主)〉A.f9无方法错误:未定义的方法f9' for A:Class from (pry):40:inpry'[21] pry(main)〉a.f9无方法错误:未定义的方法f9' for #<A:0x007fb70717c0d0> from (pry):41:inpry'

<<ERROR3>>

[22]查询(主)〉[23]查询(主)〉A.f7无方法错误:未定义的方法f7' for A:Class from (pry):42:inpry'[24] pry(main)〉a.f7无方法错误:未定义的方法f7' for #<A:0x007fb70717c0d0> from (pry):43:inpry'[25] pry(main)〉A.f9无方法错误:未定义的方法f9' for A:Class from (pry):44:inpry'[26] pry(main)〉a.f9无方法错误:未定义的方法f9' for #<A:0x007fb70717c0d0> from (pry):45:inpry'
您能否帮助指出这些错误究竟是什么:参考〈〉标记
1.看到〈〉,我的问题:
在一个对象中定义self.method有意义吗?如果没有,为什么没有warning/error?如果有意义,对于一个对象的self.method意味着什么?
1.〉,为什么f7不能同时使用A的类和对象来调用?
1.〉,为什么f9不能同时使用A的类和对象来调用?
再讨论一下:
〈〉

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块中定义方法,它不会达到相同的目标,这里有什么区别?

A.f4

A.f1

A.f5

A.f1

Aidof1仍然返回"f1"。我发现如果你使用def,那么方法就被定义并与特征类示例绑定,如果你使用define_method,那么方法就作为静态方法与类A绑定。
这里define_method和def的用法有什么区别?

xlpyo6sf

xlpyo6sf1#

您在这里提出的问题实际上是关于类、方法和对象在Ruby中如何工作的基础知识。
您所做的实际上只是class方法和instance方法之间的区别。
instance方法在最基本的意义上意味着只能从示例化对象的级别调用该方法。
这就是你在这里看到的语法。

class A
  def hi
    "hi"
  end
end

在本例中,方法hi是类A的一个示例方法,因此我们必须示例化一个对象来调用它。为了避免混淆大小写a和A,我将使用foo而不是a

foo = A.new
foo.hi

现在我们已经创建了Ainstance foo,因此我们可以在foo上调用方法hi
接下来我们有class方法,或者可以在类的级别调用的方法,在Ruby中有多种语法可以实现这一点,以下所有语法在语义上都是等价的。

class A
  def self.hello
    "hello"
  end
end

class A
  def A.hello
    "hello"
  end
end

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

上一个版本使用class << self的好处之一是我们可以在那一节定义多个方法,我根据我的工作在上面的最后两个方法之间来回切换,几乎从来没有使用过第一个版本,但这只是个人喜好。
现在,到目前为止,我所说的几乎都是Ruby对标准OO类/方法定义的实现,类似的概念将在您使用过的任何其他OO语言C++、Java、C#等中找到。
最后一个让您感到困惑的地方是Ruby在哪里引入了元类的概念,或者它们在Ruby中通常被称为“特征类”。
这基本上意味着在类和示例之间存在一个你没有定义的类,这有点混乱;请阅读此处以了解更多信息(http://en.wikipedia.org/wiki/Metaclass)。
但这允许我们直接在示例上定义方法,这样方法就只存在于该示例上,而不存在于类的其他示例上。
这就是下面的语法的用武之地。

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

现在只为示例foo重写了方法hi,这也可以用来定义只存在于示例中的全新方法。

class << foo
  def bar
    "bar"
  end
end

现在,如果我们示例化A的一个新示例

b = A.new

方法bar在B上不存在;它只存在于示例foo上。

相关问题