【发布时间】:2019-10-16 14:14:32
【问题描述】:
有一些代码:
def func
def func
1
end
end
然后我在irb 中尝试以下操作:
func
func.func
func
并得到结果:
:func
1
1
谁能解释一下发生了什么?我有点理解第一个输出,但不是后者。谢谢!
【问题讨论】:
标签: ruby metaprogramming
有一些代码:
def func
def func
1
end
end
然后我在irb 中尝试以下操作:
func
func.func
func
并得到结果:
:func
1
1
谁能解释一下发生了什么?我有点理解第一个输出,但不是后者。谢谢!
【问题讨论】:
标签: ruby metaprogramming
您在全局范围内的方法内定义方法。方法定义返回一个符号及其名称。
func 时,它会被内部func 重新定义。这就是为什么随后对func 的调用返回1。func.func 的原因。尝试定义其他方法,您将能够在任何符号上调用它:def func
def func
1
end
end
def a
'a'
end
func.a
# 'a'
:asd.a
# 'a'
【讨论】:
func 上调用a,它返回private method 'a' called for :func:Symbol (NoMethodError),Ruby 2.6.1p33 [x86_64-darwin18]。
Kernel 中。 1.a 可以工作,:foo.a 可以工作,"foo".a 可以通过在全局命名空间中定义 a 方法来工作。当调用foo.foo 时,您只需在第一个foo 调用的返回值上调用foo。
这很复杂。这确实不应该在 Ruby 中工作,但确实可以。
def func 在顶层定义了一个全局方法。但是这在 Ruby 中是如何工作的呢?它实际上使用了两个技巧:
Object,以便您可以从任何地方调用它(因为self 始终是Object)private 方法,因为私有方法的规则是它们不能有“显式接收器”,即您必须调用它们而不在它们前面放置对象和点。这是为了防止出现错误,例如我认为类 Foo 有自己的 puts 方法,但实际上没有,Foo.new.puts 会引发错误,而不是调用全局 puts。所以如果你的代码只是
def func
1
end
1.func
它会崩溃,因为您试图在对象 1 上调用私有方法。
但是这就是奇怪的地方。如果你在另一个方法中定义一个方法,它定义了一个普通实例方法,就好像它根本没有嵌套一样
class A
def outer
def inner
3
end
end
end
x = A.new
y = A.new
x.outer
y.inner # calls the method defined by x
这并不是 Ruby 中真正的有意设计,而更像是一种奇怪情况的后备方案。 lead designer 不喜欢它甚至可能
...嵌套方法定义的当前行为是无用的。它应该过时以开辟未来的可能性(我会投票赞成警告)。
你的代码表现得如此奇怪,因为你在顶层执行它,self 只是一个Object:
def func # normal private method in Object
def func # adds a normal instance method to the current class i.e. Object
1
end
end
Object.private_methods.include?(:func) # true
func # returns :func, but also now re-defines func to be a normal method on Object
Object.private_methods.include?(:func) # false
func.func # same as 1.func, which is OK because it's not private
【讨论】: