使用以下代码,我可以通过哪种方式从@arr
访问Child
?
class Parent
class << self
def create_singleton_variable
@arr = [1,2,3]
end
def arr
@arr
end
end
end
class Child < Parent
def get
puts self.arr
end
def self.get
puts self.arr
end
end
p "class method call #{Child.get}"
#=> ➜ ruby child.rb
#=> "class method call "
c = Child.new
p "instance call #{c.get}"
#=> ➜ ruby child.rb
#=> Traceback (most recent call last):
#=> 1: from child.rb:24:in `<main>'
#=> child.rb:15:in `get': undefined method `arr' for #<Child:0x00007fe0eb02e7d8> (NoMethodError)
我也尝试了许多其他方法,但是不需要在这里发布它们。
编辑该问题,因为看来我确实需要更多上下文:
我正在尝试将模块添加到Thor
框架中。然后,我想访问this bit of code
module ThorExtensions
module Thor
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "Start Completion"
p self
p self.superclass
p self.class.superclass.subcommands
puts "End Completion"
end
end
end
end
end
结果
Start Completion
Debug
Thor
bundler: failed to load command: exe/pt (exe/pt)
NoMethodError: undefined method `subcommands' for Module:Class
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/thor_extensions/completion_generation.rb:13:in `completion'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/debug/debug.rb:24:in `<class:Debug>'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/debug/debug.rb:4:in `<top (required)>'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/pt.rb:5:in `require'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/pt.rb:5:in `<top (required)>'
exe/pt:13:in `require'
exe/pt:13:in `<top (required)>'
那当然不是我想要的。看来我的问题可能是与前置?
我似乎在解释自己的问题时做得很糟糕。这是一个显示我的问题的完整示例。我相信这是由于在类中添加某些内容实际上是如何在调用堆栈中首先创建另一个Class的。我希望我实际上仍然能够以某种方式访问此方法。
class Parent
class << self
def create_singleton_variable
@arr = [1,2,3]
puts "arr is initialized #{@arr}"
end
# ... lots of code here.
def arr
puts "arr is #{@arr.inspect}"
@arr
end
end
end
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "self.superclass.arr == #{self.superclass.arr.inspect}" # unable to access superclass arr
puts "self.class.superclass.arr == #{self.class.superclass.arr}" # likewise, unable to access Child's metaclass superclass
rescue Exception => e
# do nothing, this is just so you can see arr is actually initialized in the context of the Child
p e
end
end
end
Parent.prepend CompletionGeneration
class Child < Parent
create_singleton_variable
completion
arr
end
Child.new
输出结果
➜ ruby child.rb
arr is initialized [1, 2, 3]
arr is nil
self.superclass.arr == nil
#<NoMethodError: undefined method `arr' for Module:Class>
arr is [1, 2, 3]
此代码应简单地复制即可粘贴。
答案 0 :(得分:1)
这是您的代码,稍作修改。
class Parent
def self.create_singleton_variable
@arr = [1,2,3]
end
def self.arr
puts "self = #{self} in the getter for @arr"
@arr
end
end
class Child < Parent
def get
puts self.arr
end
def self.get
puts self.arr
end
end
我以更传统的方式写了Parent
。除了添加了puts
语句外,它与问题中包含的内容相同。
首先,打个头:Kernel#puts-任何东西都会返回nil
。您需要从两种方法中删除puts
:
class Child < Parent
def get
self.arr
end
def self.get
self.arr
end
end
Parent.create_singleton_variable
#=> [1, 2, 3]
Child.get.nil?
self = Child in the getter for @arr
#=> true
我们看到在arr
的类方法Child
调用的getter get
中,self
等于Child
,因此该方法寻找一个@arr
的类实例变量Child
而不是Parent
的类实例变量。由于尚未初始化此类实例变量,因此将返回nil
。
您需要以下内容。
class Parent
class << self
def create_singleton_variable
@arr = [1,2,3]
end
def arr
puts "self = #{self} in the getter for @arr"
@arr
end
end
end
class Child < Parent
def get
self.class.superclass.arr
end
def self.get
superclass.arr
end
end
与问题中给出的关键区别在于Class#superclass将范围(即self
)更改为Parent
。
我们看到获得了预期的结果。
Child.get
self = Parent in the getter for @arr
#=> [1, 2, 3]
Child.new.class.superclass.arr
self = Parent in the getter for @arr
#=> [1, 2, 3]
一个常见的误解是定义为Child
的{{1}}类方法将调用getter def self.get; self.arr; end
,并因此返回Parent::arr
的实例变量{{1}的值}。调用的是Parent
,但是该方法是从@arr
继承的,正在检索的是Child::arr
的类实例变量Parent
,这很微妙。 ,但重要的是区别。
编辑2
第一个观察结果是Child
可以用更传统(且完全等效)的方式编写。
@arr
无论其编写方式如何,在父类上调用任一类方法时,Parent
都将等于class Parent
def self.create_singleton_variable
@arr = [1,2,3]
puts "arr is initialized #{@arr}"
end
def self.arr
puts "arr is #{@arr.inspect}"
@arr
end
end
。因此,第一个将创建类实例变量self
。
Parent
现在让我们为@arr
创建一个类变量。
Parent.methods(false)
#=> [:create_singleton_variable, :arr]
Parent.instance_variables
#=> []
Parent.ancestors
#=> [Parent, Object, Kernel, BasicObject]
现在让我更改Parent
的值。
Parent.create_singleton_variable
# arr is initialized [1, 2, 3]
Parent.instance_variables
#=> [:@arr]
接下来,创建类@arr
,但尚未在模块前添加
Parent.instance_variable_set(:@arr, ['dog', 'cat'])
#=> ["dog", "cat"]
Parent.arr
# arr is ["dog", "cat"]
#=> ["dog", "cat"]
没有惊喜。接下来加载模块。
Child
(注意class Child < Parent
create_singleton_variable
arr
end
arr is initialized [1, 2, 3]
arr is [1, 2, 3]
Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]
Child.instance_variables
#=> [:@arr]
Child.instance_variable_get(:@arr)
#=> [1, 2, 3]
中不需要module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "self=#{self}"
puts "superclass=#{superclass}"
puts "self.class=#{self.class}"
puts "self.class.superclass == #{self.class.superclass}"
puts "superclass.arr == #{superclass.arr.inspect}"
puts "self.class.superclass.arr == #{self.class.superclass.arr}"
rescue Exception => e
# do nothing, this is just so you can see arr is actually
# initialized in the context of the Child
puts "Exception => e=#{e}"
end
end
end
)现在将此模块放在self.
之前。
"superclass.arr == #{superclass.arr.inspect}"
以等于Parent
的{{1}}触发回调模块方法Parent.prepend CompletionGeneration
Parent.ancestors
#=> [CompletionGeneration, Parent, Object, Kernel, BasicObject]
Parent.methods.include?(:completion)
#=> true
Child.ancestors
#=> [Child, CompletionGeneration, Parent, Object, Kernel, BasicObject]
Child.methods.include?(:completion)
#=> true
,导致CompletionGeneration::prepended
的单例类以base
开头,从而添加了该类方法Parent
。由于Parent
以前没有使用ClassMethods
或Parent::completion
的同名方法,因此具有相同的效果。此外,可以使用Parent
回调代替prepend
并执行include
。也许Parent.singleton_class.include ClassMethods
在这里通常用于included(base)
可能具有该名称的类方法的情况。 1
现在执行以下操作。
Parent.extend ClassMethods
在以下时间引发异常
prepend
正在执行。等于
Parent
但是当然Child.completion
self=Child
superclass=Parent
self.class=Class
self.class.superclass == Module
arr is ["dog", "cat"]
superclass.arr == ["dog", "cat"]
Exception => e=undefined method `arr' for Module:Class
没有模块方法puts "self.class.superclass.arr == #{self.class.superclass.arr}"
。
1鉴于puts "self.class.superclass.arr == #{Module.arr}"
,在Module
前面加上模块只会导致arr
的孩子成为Child.ancestors
(而不是Parent
)模块;也就是说,如果子项在前置之前已经具有方法Parent
,则该方法将不会被模块的同名方法所抢占。