当我在 IRB 中声明时,为啥我的***方法在所有类上都是公共的(而不是私有的)?
Posted
技术标签:
【中文标题】当我在 IRB 中声明时,为啥我的***方法在所有类上都是公共的(而不是私有的)?【英文标题】:Why is my top-level method public (as opposed to private) on all classes when I declare it in IRB?当我在 IRB 中声明时,为什么我的***方法在所有类上都是公共的(而不是私有的)? 【发布时间】:2019-05-27 17:31:34 【问题描述】:我目前正在阅读“The Well-Grounded Rubyist”,在第 196 页我看到以下内容:
假设你在顶层定义了一个方法:
def talk puts "Hello" end
....
您在顶层定义的方法存储为私有
Object
类的实例方法。之前的代码是 相当于这个:class Object private def talk puts "Hello" end end
...
为了说明,让我们扩展
talk
示例。又来了, 用一些代码来练习它:puts "Trying 'talk' with no receiver..." talk puts "Trying 'talk' with an explicit receiver..." obj = Object.new obj.talk
第一次调用
talk
成功;第二次失败并致命 错误,因为它尝试使用显式调用私有方法 接收器。
我想在我的本地重现这个,所以我把上面的代码放在我创建的 Ruby 文件中。我确实得到了书中提到的结果:
$ ruby talk.rb
Trying 'talk' with no receiver...
Hello
Trying 'talk' with an explicit receiver...
Traceback (most recent call last):
talk.rb:22:in `<main>': private method `talk' called for #<Object:0x00007f9a8499c3e0> (NoMethodError)
我还尝试了以下方法,它产生了与通过 Ruby 解释器运行代码相同的错误:
irb(main):008:0> load 'talk.rb'
Trying 'talk' with no receiver...
Hello
Trying 'talk' with an explicit receiver...
Traceback (most recent call last):
4: from /Users/richiethomas/.rbenv/versions/2.5.3/bin/irb:11:in `<main>'
3: from (irb):8
2: from (irb):8:in `load'
1: from talk.rb:22:in `<top (required)>'
NoMethodError (private method `talk' called for #<Object:0x00007ffb219c95e0>)
接下来,我在irb
中尝试了同样的代码,这次我得到了以下奇怪的结果:
irb(main):001:0> def talk
irb(main):002:1> puts "Hello"
irb(main):003:1> end
=> :talk
irb(main):004:0> puts "Trying 'talk' with no receiver..."
Trying 'talk' with no receiver...
=> nil
irb(main):005:0> talk
Hello
=> nil
irb(main):006:0> puts "Trying 'talk' with an explicit receiver..."
Trying 'talk' with an explicit receiver...
=> nil
irb(main):007:0> Object.new.talk
Hello
=> nil
如您所见,在最后一个代码示例中,我能够调用Object.new.talk
并让它打印Hello
,就好像.talk
是Object
实例上的公共方法一样。
我的问题是 - 为什么 talk
方法在我直接在 REPL 中实现时在 Object 类上是公共的,但当我在文件中实现它并将其加载到 REPL 时是私有的(以及当我运行它时)通过 Ruby 解释器直接在我的 CLI 中使用相同的文件)?
【问题讨论】:
参见this article: 'IRB 将***范围内的方法绑定到 main 作为公共方法,以方便使用。顺便说一句,我认为如果你只提到“irb”而不是“irb REPL”会更清楚。 @CarySwoveland-为了清楚起见,我编辑了我的问题。此外,查看the IRB source code 后,我看到以下内容:Because irb evaluates input immediately after it is syntactically complete, the results may be slightly different than directly using Ruby.
。这可能是指您的文章提到的公共级别的绑定。
我认为是指ruby 'src.rb'
没有问题但irb无法处理的多行语句。例如,irb 会在一行上阻塞arr.map(&:to_i)
,在下一行阻塞.sum
(因为它不知道第一行还在继续)。这里使用 irb 你需要在第一行写arr.map(&:to).
,在下一行写“sum”。
这是有道理的。另外,我发现this link 这似乎与您链接的文章中的观点相呼应。无论如何,关键的要点似乎是 IRB 不会像 MRI 那样“私有化”在其***范围内声明的任何方法。我很想知道为什么会这样。
【参考方案1】:
irb
和 pry
(旁注: 我强烈建议使用后者)调整它们的输入以将所有方法声明为公共(在 E
REP
循环的阶段):
▶ def foo; end
#⇒ :foo
▶ public_methods.grep /foo/
#⇒ [:foo]
就是这样,没有魔法。
这样做主要是为了简化在场景中使用它,当一个人在这里定义方法,然后希望它可以从例如那里。在REPL
中,让所有内容随处可访问是值得的。
def pretty_print; self.inspect; end
class A; ...; end
class B; ...; end
A.new.pretty_print
B.new.pretty_print
玩沙盒的时候不要太注意封装、SRP等。
一般来说,这就像使用通用帮助器声明模块并在任何地方免费包含它。
【讨论】:
我在 irb 中添加了以下内容,class Object; puts self; def print1; end; end;
和 puts self; def print2; end;
都为 Object
定义了实例方法,但在两种情况下都检查了 self,一个显示类 Object
,另一个显示 Object 类实例 @ 987654333@。如果我遗漏了什么,请澄清。
main
是由 Object
的 IRB 实例隐式定义的。
如果添加private :pretty_print
,该方法确实会成为Object
的私有实例方法,所以它仍然可以在顶层使用。它不能在其他类中使用,但我不知道为什么要在 irb 中这样做。也就是说,为什么A.new.pretty_print
?也许您可以详细说明为什么 main
中定义的方法公开是一种“便利”。
@CarySwoveland 因为您无需为常见行为定义模块并将此模块包含在任何地方。
没错,但这不是一个很好的节省。我会创建包含在类中的模块(在 irb 中),部分原因是我希望代码在文件中执行时能够工作。我从来没有像你提到的那样使用过irb。 (有吗?)也许我应该考虑这样做。以上是关于当我在 IRB 中声明时,为啥我的***方法在所有类上都是公共的(而不是私有的)?的主要内容,如果未能解决你的问题,请参考以下文章
为啥当我在函数中声明一个名称为全局数组的局部数组时,bash 会引发未绑定变量警告?
为啥当我在我的 ArrayList 上输入确切的名称时,“包含”方法返回 false? [复制]
当我在 Thread 对象上调用 run() 时,为啥我的 Java 程序会泄漏内存?