Ruby 元类的疯狂

发布于 2024-08-24 18:55:01 字数 1189 浏览 10 评论 0原文

我被困住了。我正在尝试动态定义类方法,但无法理解 ruby​​ 元类模型。考虑下面的类:

class Example

  def self.meta; (class << self; self; end); end

  def self.class_instance; self; end

end

Example.class_instance.class # => Class
Example.meta.class           # => Class

Example.class_instance  == Example      # => true
Example.class_instance  == Example.meta # => false

显然这两个方法都返回一个 Class 的实例。但这两个例子 不一样。它们也有不同的祖先:

Example.meta.ancestors            # => [Class, Module, Object, Kernel]
Example.class_instance.ancestors  # => [Example, Object, Kernel]

区分元类和类实例有什么意义?

我发现,我可以发送 :define_method 到元类来动态定义一个方法,但如果我尝试将它发送到类实例,它将不起作用。至少我可以解决我的问题,但我仍然想了解为什么它会这样工作。

更新于 2010 年 3 月 15 日 13:40

以下假设是否正确?

  • 如果我有一个调用 self.instance_eval 并定义方法的实例方法,它只会影响该类的特定实例。
  • 如果我有一个实例方法,它调用 self.class.instance_eval (这与调用 class_eval 相同)并定义一个方法,它将影响该特定类的所有实例,从而产生一个新的实例方法。
  • 如果我有一个类方法调用instance_eval并定义一个方法,它将为所有实例生成一个新的实例方法。
  • 如果我有一个类方法,它在元/特征类上调用instance_eval并定义一个方法,它将产生一个类方法。

我想这对我来说开始有意义了。如果类方法中的 self 指向本征类,那么它肯定会限制您的可能性。如果是这样,就不可能从类方法内部定义实例方法。这是正确的吗?

I'm stuck. I'm trying to dynamically define a class method and I can't wrap my head around the ruby metaclass model. Consider the following class:

class Example

  def self.meta; (class << self; self; end); end

  def self.class_instance; self; end

end

Example.class_instance.class # => Class
Example.meta.class           # => Class

Example.class_instance  == Example      # => true
Example.class_instance  == Example.meta # => false

Obviously both methods return an instance of Class. But these two instances
are not the same. They also have different ancestors:

Example.meta.ancestors            # => [Class, Module, Object, Kernel]
Example.class_instance.ancestors  # => [Example, Object, Kernel]

What's the point in making a difference between the metaclass and the class instance?

I figured out, that I can send :define_method to the metaclass to dynamically define a method, but if I try to send it to the class instance it won't work. At least I could solve my problem, but I still want to understand why it is working this way.

Update Mar 15, 2010 13:40

Are the following assumptions correct.

  • If I have an instance method which calls self.instance_eval and defines a method, it will only affect the particular instance of that class.
  • If I have an instance method which calls self.class.instance_eval (which would be the same as calling class_eval) and defines a method it will affect all instances of that particular class resulting in a new instance method.
  • If I have a class method which calls instance_eval and defines a method it will result in a new instance method for all instances.
  • If I have a class method which calls instance_eval on the meta/eigen class and defines a method it will result in a class method.

I think it starts to make sense to me. It would certainly limit your possibilities if self inside an class method would point to the eigen class. If so it would not be possible to define an instance method from inside a class method. Is that correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

裸钻 2024-08-31 18:55:01

使用 instance_eval 时,动态定义单例方法很简单:

Example.instance_eval{ def square(n); n*n; end }
Example.square(2) #=> 4
# you can pass instance_eval a string as well.
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27

至于上面的区别,您会混淆两件事:

您定义的元类,在 Ruby 社区中称为 singelton 类特征类。该单例类是您可以添加类(单例)方法的类。

至于你试图使用class_instance方法定义的类实例,只不过是类本身,为了证明这一点,只需尝试向类添加一个实例方法Example并通过检查该方法的存在来检查您定义的 class_instance 方法是否返回类 Example 本身:

class Example
  def self.meta; (class << self; self; end); end
  def self.class_instance; self; end
  def hey; puts hey; end
end

Example.class_instance.instance_methods(false) #=> ['hey']

无论如何,当您想要添加类方法时,为您求和,只需将它们添加到该元类中即可。至于class_instance方法没用,去掉即可。

不管怎样,我建议你阅读这篇文章来掌握Ruby反射系统的一些概念。

更新

我建议您阅读这篇好文章:Ruby 的 instance_eval 和 class_eval 的乐趣
不幸的是,class_evalinstance_eval令人困惑,因为它们以某种方式违背了它们的命名!

Use ClassName.instance_eval to define class methods.

Use ClassName.class_eval to define instance methods.

现在回答你的假设:

如果我有一个实例方法
调用 self.instance_eval 并定义一个
方法,它只会影响
该类的特定实例。

是的:

class Foo
  def assumption1()
    self.instance_eval("def test_assumption_1; puts 'works'; end")
  end
end

f1 = Foo.new
f1.assumption1
f1.methods(false) #=> ["test_assumption_1"]
f2 = Foo.new.methods(false) #=> []

如果我有一个实例方法
调用 self.class.instance_eval (其中
与调用相同
class_eval) 并定义了一个方法 it
将影响该事件的所有实例
特定的类产生一个新的
实例方法。

在该上下文中,没有 instance_eval 会在类本身上定义单例方法(不是实例方法):

class Foo
  def assumption2()
    self.class.instance_eval("def test_assumption_2; puts 'works'; end")
  end
end

f3 = Foo.new
f3.assumption2
f3.methods(false) #=> []
Foo.singleton_methods(false) #=> ["test_assumption_2"]

为此,请用上面的 class_eval 替换 instance_eval

如果我有一个类方法调用
instance_eval 并定义了一个方法 it
将产生一个新的实例方法
对于所有情况。

不:

class Foo
  instance_eval do
    def assumption3()
      puts 'works'
    end
  end
end

Foo.instance_methods(false) #=> []

Foo.singleton_methods(false) #=> ["assumption_3"]

这将创建单例方法,而不是实例方法。为此,请将上面的 instance_eval 替换为 class_eval

如果我有一个类方法调用
元/特征类上的instance_eval
并定义一个方法,它将导致
一个类方法。

好吧,不,这会产生如此复杂的东西,因为它将向单例类添加单例方法,我认为这不会有任何实际用途。

Defining a singleton method dynamically is simple when you use instance_eval:

Example.instance_eval{ def square(n); n*n; end }
Example.square(2) #=> 4
# you can pass instance_eval a string as well.
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27

As for the difference above, you are confusing 2 things:

The meta class defined by you, is what called in Ruby community as singelton class or eigen class. That singleton class is the class that you can add class(singleton) methods to.

As for the class instance you are trying to define using the class_instance method, is nothing but the class itself, to prove it, just try adding an instance method to the class Example and check if the class_instance method defined by you returns the class Example itself by checking the existence of that method:

class Example
  def self.meta; (class << self; self; end); end
  def self.class_instance; self; end
  def hey; puts hey; end
end

Example.class_instance.instance_methods(false) #=> ['hey']

Anyway to sum it for you, when you want to add class methods, just add them to that meta class. As for the class_instance method is useless, just remove it.

Anyway I suggest you read this post to grasp some concepts of Ruby reflection system.

UPDATE

I suggest you read this nice post: Fun with Ruby's instance_eval and class_eval,
Unfortunately class_eval and instance_eval are confusing because they somehow work against their naming!

Use ClassName.instance_eval to define class methods.

Use ClassName.class_eval to define instance methods.

Now answering your assumptions:

If I have an instance method which
calls self.instance_eval and defines a
method, it will only affect the
particular instance of that class.

yes:

class Foo
  def assumption1()
    self.instance_eval("def test_assumption_1; puts 'works'; end")
  end
end

f1 = Foo.new
f1.assumption1
f1.methods(false) #=> ["test_assumption_1"]
f2 = Foo.new.methods(false) #=> []

If I have an instance method which
calls self.class.instance_eval (which
would be the same as calling
class_eval) and defines a method it
will affect all instances of that
particular class resulting in a new
instance method.

no instance_eval in that context will define singleton methods(not instance ones) on the class itself:

class Foo
  def assumption2()
    self.class.instance_eval("def test_assumption_2; puts 'works'; end")
  end
end

f3 = Foo.new
f3.assumption2
f3.methods(false) #=> []
Foo.singleton_methods(false) #=> ["test_assumption_2"]

For that to work replace instance_eval with class_eval above.

If I have a class method which calls
instance_eval and defines a method it
will result in a new instance method
for all instances.

Nope:

class Foo
  instance_eval do
    def assumption3()
      puts 'works'
    end
  end
end

Foo.instance_methods(false) #=> []

Foo.singleton_methods(false) #=> ["assumption_3"]

That will make singleton methods, not instance methods. For that to work replace instance_eval with class_eval above.

If I have a class method which calls
instance_eval on the meta/eigen class
and defines a method it will result in
a class method.

well no, that will make so sophisticated stuff, as it will add singleton method to the singleton class, I don't think that will have any practical use.

瘫痪情歌 2024-08-31 18:55:01

如果您在上定义方法,则可以在其对象上调用该方法。它是一个实例方法

class Example
end

Example.send :define_method, :foo do
  puts "foo"
end

Example.new.foo
#=> "foo"

如果您在元类上定义方法,则可以在上调用该方法。这类似于其他语言中的类方法或静态方法的概念。

class Example
  def self.metaclass
    class << self
      self
    end
  end
end

Example.metaclass.send :define_method, :bar do
  puts "bar"
end

Example.bar
#=> "bar"

元类存在的原因是因为您可以在 Ruby 中执行此操作:

str = "hello"
class << str
  def output
    puts self
  end
end

str.output
#=> "hello"

"hi".output
# NoMethodError

如您所见,我们定义了一个仅可用于 String 的一个实例的方法。我们定义此方法的对象称为元类。在方法查找链中,在搜索对象的类之前首先访问元类。

如果我们将 String 类型的对象替换为 Class 类型的对象,您可以想象为什么这意味着我们只在特定的上定义一个方法 em> 类,并非所有类。

当前上下文和 self 之间的差异很微妙,您可以 如果您有兴趣,请阅读更多内容

If you define a method on a class, it can be invoked on its objects. It is an instance method.

class Example
end

Example.send :define_method, :foo do
  puts "foo"
end

Example.new.foo
#=> "foo"

If you define a method on a metaclass, it can be invoked on the class. This is similar to the concept of a class method or static method in other languages.

class Example
  def self.metaclass
    class << self
      self
    end
  end
end

Example.metaclass.send :define_method, :bar do
  puts "bar"
end

Example.bar
#=> "bar"

The reason that metaclasses exist is because you can do this in Ruby:

str = "hello"
class << str
  def output
    puts self
  end
end

str.output
#=> "hello"

"hi".output
# NoMethodError

As you can see, we defined a method that is only available to one instance of a String. The thing that we defined this method on is called the metaclass. In the method lookup chain, the metaclass is accessed first before searching the object's class.

If we replace the object of type String with an object of type Class, you can imagine why this means we're only defining a method on a specific class, not on all classes.

The differences between the current context and self are subtle, you can read more if you're interested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文