类对象、单例类

发布于 2024-12-22 23:53:11 字数 926 浏览 1 评论 0原文

我在 ruby​​ 中玩元编程,我有一个问题。我有一堂课:

class Klass
  class << self
    @x = "yeah"
  end
end
b = Klass.new
a = class << Klass; self; end
a.instance_eval "@x"           #=> yeah
Klass.instance_eval "@x"       #=> nil

为什么?在变量 a 中我有一个单例类,对吗?并且在单例类的上下文中执行Klass.instance_eval

Klass.instance_eval "def yeah; puts 10; end"
Klass.yeah                     #=> 10

另外,解释器中的Klass指向类的上下文,是吗?并且 a 指向单例类的上下文? 哪个表示 a.class_evala.instance_eval?我明白:

a.instance_eval "def pops; puts 0; end"
a.class_eval "def popsx; puts 1; end"
a.pops                         #=> 0
a.popsx                        # FAIL
Klass.pops                     # FAIL
Klass.popsx                    #=> 1
b.pops; b.popsx                # DOUBLE FAIL

但我不明白这一点。谢谢!

I play with metaprogramming in ruby and I have a question. I have a class:

class Klass
  class << self
    @x = "yeah"
  end
end
b = Klass.new
a = class << Klass; self; end
a.instance_eval "@x"           #=> yeah
Klass.instance_eval "@x"       #=> nil

Why? In variable a I have a singleton class, right? And Klass.instance_eval exec in context of a singleton class:

Klass.instance_eval "def yeah; puts 10; end"
Klass.yeah                     #=> 10

Also, Klass in interpreter points to context of class, yes? And a points to context of a singleton class?
And which indicates a.class_eval and a.instance_eval? I do:

a.instance_eval "def pops; puts 0; end"
a.class_eval "def popsx; puts 1; end"
a.pops                         #=> 0
a.popsx                        # FAIL
Klass.pops                     # FAIL
Klass.popsx                    #=> 1
b.pops; b.popsx                # DOUBLE FAIL

and I do not understand this. Thanks!

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

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

发布评论

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

评论(2

空城缀染半城烟沙 2024-12-29 23:53:11

首先,虽然看起来eigentclass被一些人使用,但singleton class是更常见的术语。 Singleton 类包含 Ruby 中对象的特定于对象的行为。除了该单例类所属的原始对象之外,您无法创建该类的其他实例。

谈论不同类型的 eval 中的方法定义这篇文章instance_evalclass_eval 中定义的方法引入了很好的规则:

使用ClassName.instance_eval定义类方法。
使用 ClassName.class_eval 定义实例方法。

这几乎描述了情况。

有一篇关于 Class 类的实例的类、作为 Class 类的子类的单例类以及其他一些疯狂的东西(与问题没有多大关系)的文章。但是,由于您的问题可以轻松应用于常规对象及其类(并且这使得事情更容易解释),我决定删除所有内容(不过,您仍然可以在答案的修订历史记录中看到这些内容)。

让我们看看常规类和该类的实例,看看它们是如何工作的:

class A; end
a = A.new

不同类型的 eval 中的方法定义:

# define instance method inside class context
A.class_eval { def bar; 'bar'; end }
puts a.bar     # => bar
puts A.new.bar # => bar

# class_eval is equivalent to re-opening the class
class A
  def bar2; 'bar2'; end
end
puts a.bar2     # => bar2
puts A.new.bar2 # => bar2

定义特定于对象的方法:

# define object-specific method in the context of object itself
a.instance_eval { def foo; 'foo'; end }
puts a.foo # => foo

# method definition inside instance_eval is equivalent to this
def a.foo2; 'foo2'; end
puts a.foo2 # => foo2

# no foo method here
# puts A.new.foo # => undefined method `foo' for #<A:0x8b35b20>

现在让我们看看对象 a 的单例类:

# singleton class of a is subclass of A
p (class << a; self; end).ancestors
# => [A, Object, Kernel, BasicObject]

# define instance method inside a's singleton class context
class << a
  def foobar; 'foobar'; end;
end
puts a.foobar # => foobar

# as expected foobar is not available for other instances of class A
# because it's instance method of a's singleton class and a is the only
# instance of that class
# puts A.new.foobar # => undefined method `foobar' for #<A:0x8b35b20>

# same for equivalent class_eval version
(class << a; self; end).class_eval do
  def foobar2; 'foobar2'; end;
end
puts a.foobar2 # => foobar2

# no foobar2 here as well
# puts A.new.foobar2 # => undefined method `foobar2' for #<A:0x8b35b20>

现在让我们看看实例变量

# define instance variable for object a
a.instance_eval { @x = 1 }

# we can access that @x using same instance_eval
puts a.instance_eval { @x } # => 1
# or via convenient instance_variable_get method
puts a.instance_variable_get(:@x) # => 1

:现在到 class_eval 中的实例变量:

# class_eval is instance method of Module class
# so it's not available for object a
# a.class_eval { } # => undefined method `class_eval' for #<A:0x8fbaa74>

# instance variable definition works the same inside
# class_eval and instance_eval
A.instance_eval { @y = 1 }
A.class_eval    { @z = 1 }

# both variables belong to A class itself
p A.instance_variables # => [:@y, :@z]

# instance variables can be accessed in both ways as well
puts A.instance_eval { @y } # => 1
puts A.class_eval    { @z } # => 1

# no instance_variables here
p A.new.instance_variables # => []

现在,如果将类 A 替换为类 Class,将对象 a 替换为对象 < code>Klass (在这种特殊情况下只不过是类 Class 的实例)我希望您能得到对您问题的解释。如果您还有一些,请随时询问。

First, while it seems like eigentclass is used by some people singleton class is more common term. Singleton class contains object-specific behavior for an object in Ruby. You can't create other instances of that class except the original object this singleton class belongs to.

Speaking about defining of methods inside different types of eval this article introduces nice rule for methods defined in instance_eval and class_eval:

Use ClassName.instance_eval to define class methods.
Use ClassName.class_eval to define instance methods.

That pretty much describes the situation.

There was a huge write-up about classes that are instances of Class class, their singleton classes that are subclasses of Class class and some other crazy stuff (not that much related to the problem). But as your question can be easily applied to regular objects and their classes (and it makes things much easier to explain), I decided to remove that all (though, you can still see that stuff in revisions history of the answer).

Let's look at regular class and instance of that class and see how that all works:

class A; end
a = A.new

Method definitions inside different types of eval:

# define instance method inside class context
A.class_eval { def bar; 'bar'; end }
puts a.bar     # => bar
puts A.new.bar # => bar

# class_eval is equivalent to re-opening the class
class A
  def bar2; 'bar2'; end
end
puts a.bar2     # => bar2
puts A.new.bar2 # => bar2

Defining object-specific methods:

# define object-specific method in the context of object itself
a.instance_eval { def foo; 'foo'; end }
puts a.foo # => foo

# method definition inside instance_eval is equivalent to this
def a.foo2; 'foo2'; end
puts a.foo2 # => foo2

# no foo method here
# puts A.new.foo # => undefined method `foo' for #<A:0x8b35b20>

Let's now look at singleton class of object a:

# singleton class of a is subclass of A
p (class << a; self; end).ancestors
# => [A, Object, Kernel, BasicObject]

# define instance method inside a's singleton class context
class << a
  def foobar; 'foobar'; end;
end
puts a.foobar # => foobar

# as expected foobar is not available for other instances of class A
# because it's instance method of a's singleton class and a is the only
# instance of that class
# puts A.new.foobar # => undefined method `foobar' for #<A:0x8b35b20>

# same for equivalent class_eval version
(class << a; self; end).class_eval do
  def foobar2; 'foobar2'; end;
end
puts a.foobar2 # => foobar2

# no foobar2 here as well
# puts A.new.foobar2 # => undefined method `foobar2' for #<A:0x8b35b20>

Now let's look at instance variables:

# define instance variable for object a
a.instance_eval { @x = 1 }

# we can access that @x using same instance_eval
puts a.instance_eval { @x } # => 1
# or via convenient instance_variable_get method
puts a.instance_variable_get(:@x) # => 1

And now to instance variables inside class_eval:

# class_eval is instance method of Module class
# so it's not available for object a
# a.class_eval { } # => undefined method `class_eval' for #<A:0x8fbaa74>

# instance variable definition works the same inside
# class_eval and instance_eval
A.instance_eval { @y = 1 }
A.class_eval    { @z = 1 }

# both variables belong to A class itself
p A.instance_variables # => [:@y, :@z]

# instance variables can be accessed in both ways as well
puts A.instance_eval { @y } # => 1
puts A.class_eval    { @z } # => 1

# no instance_variables here
p A.new.instance_variables # => []

Now if you replace class A with class Class and object a with object Klass (that in this particular situation is nothing more than instance of class Class) I hope you'll get explanation to your questions. If you still have some feel free to ask.

┊风居住的梦幻卍 2024-12-29 23:53:11

很难完全回答您的问题(有关 Ruby 类模型的彻底解释,请参阅 Dave Thomas 的出色演示),不过:

使用 class_eval,您实际上定义了实例方法 - 就好像您处于 班级。例如:

class Klass
end

Klass.class_eval do
  def instance_method
    puts 'instance method'
  end
end

obj = Klass.new
obj.instance_method  # instance method

使用 instance_eval,您实际上定义了类方法 - 就好像您位于给定对象的单例(eigenclass)类的主体内(注意类在 Ruby 中也是对象)。例如:

Klass.instance_eval do
  def class_method
    puts 'class method'
  end
end

Klass.class_method  # class method

在您的情况下:

Klass.instance_eval "@x" 不起作用,因为 @x 不是 Klass 的一部分,它是 Klass 单例类的一部分:

class Klass
  class << self
    @x = "yeah"
  end

  puts @x
end

# prints nothing

< code>a.instance_eval "@x" 工作正常,因为您在 a 单例类的上下文中计算“@x”,该单例类与您在其中使用的 Klass 类的单例类连接定义了@x实例变量。单例类如何互连可以通过以下示例来说明:

class Foo
end

f = class << Foo; self; end
g = class << Foo; self; end

f.instance_eval "def yeah; puts 'yeah'; end"

g.yeah  # yeah

g.instance_eval "def ghee; puts 'ghee'; end"

f.ghee  # ghee

Klass.instance_eval "def yes; put 10; end" 定义一个“普通”类方法。因此,Klass.yeah 工作正常(请参阅前面示例中的 Klass.class_method)。

a.instance_eval "def pops; puts 0; end" 定义 a 单例类的类方法。因此,a.pops实际上意味着调用pops类方法(同样,这就像调用Klass.class_method)。

a.popsx 不起作用,因为您首先必须创建 a 的实例才能在其上调用 popsx(但不可能创建 a 的新实例)单例类)。

Klass.pops 不起作用,因为 Klass 的单例类中没有定义任何 pops 方法(pops 是在 a 中定义的) 的单例类)。

Klass.popsx 之所以有效,是因为使用 a.class_eval "def popsx; put 1; end" 您已经定义了 popsx 实例方法,然后可以在 Klass 对象上调用该方法。在某种程度上,它与这个例子类似:

class Other
end

o = Other.new

Other.class_eval "def yeah; puts 'yeah'; end"

o.yeah  # yeah

希望它有帮助。

It's hard to completely answer your question (for a thorough explanation of Ruby's class model, look at Dave Thomas' excellent presentation), nevertheless:

With class_eval, you actually define instance methods - it's as if you were inside the body of the class. For example:

class Klass
end

Klass.class_eval do
  def instance_method
    puts 'instance method'
  end
end

obj = Klass.new
obj.instance_method  # instance method

With instance_eval, you actually define class methods - it's as if you were inside the body of the singleton (eigenclass) class of the given object (nota bene that classes are objects too in Ruby). For example:

Klass.instance_eval do
  def class_method
    puts 'class method'
  end
end

Klass.class_method  # class method

And in your case:

Klass.instance_eval "@x" does not work because @x is not part of Klass, it's part of Klass' singleton class:

class Klass
  class << self
    @x = "yeah"
  end

  puts @x
end

# prints nothing

a.instance_eval "@x" works fine because you evaluate "@x" in the context of the a singleton class that is connected with the singleton class of Klass class in which you defined the @x instance variable. How singleton classes can be interconnected can be illustrated by this example:

class Foo
end

f = class << Foo; self; end
g = class << Foo; self; end

f.instance_eval "def yeah; puts 'yeah'; end"

g.yeah  # yeah

g.instance_eval "def ghee; puts 'ghee'; end"

f.ghee  # ghee

Klass.instance_eval "def yeah; puts 10; end" defines a 'normal' class method. Therefore Klass.yeah works fine (see Klass.class_method in the previous example).

a.instance_eval "def pops; puts 0; end" defines a class method on the a singleton class. Therefore, a.pops actually means calling the pops class method (again, it's as if calling Klass.class_method).

a.popsx does not work because you would first have to create an instance of a to be able to call popsx on it (but is not possible to create a new instance of a singleton class).

Klass.pops does not work because there isn't any pops method defined in Klass' singleton class (pops is defined in a's singleton class).

Klass.popsx works because with a.class_eval "def popsx; puts 1; end" you have defined the popsx instance method which you then call on the Klass object. It is, in a way, similar to this example:

class Other
end

o = Other.new

Other.class_eval "def yeah; puts 'yeah'; end"

o.yeah  # yeah

Hope it helps.

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