当我在 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,就好像.talkObject 实例上的公共方法一样。

我的问题是 - 为什么 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(&amp;:to_i),在下一行阻塞.sum(因为它不知道第一行还在继续)。这里使用 irb 你需要在第一行写arr.map(&amp;:to).,在下一行写“sum”。 这是有道理的。另外,我发现this link 这似乎与您链接的文章中的观点相呼应。无论如何,关键的要点似乎是 IRB 不会像 MRI 那样“私有化”在其***范围内声明的任何方法。我很想知道为什么会这样。 【参考方案1】:

irbpry旁注: 我强烈建议使用后者)调整它们的输入以将所有方法声明为公共(在 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 会引发未绑定变量警告?

ruby 和 irb 都不能在当前目录中加载 .rb 文件

为啥当我在我的 ArrayList 上输入确切的名称时,“包含”方法返回 false? [复制]

当我在 Thread 对象上调用 run() 时,为啥我的 Java 程序会泄漏内存?

在 Winform 中,为啥当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新?

当我在 pyspark 中收集它们时,为啥我的 `binaryFiles` 是空的?