【问题标题】:Is it good practice having local variables starting with underscore?以下划线开头的局部变量是一种好习惯吗?
【发布时间】:2012-01-23 18:54:27
【问题描述】:

我刚接触 Ruby,来自 Java 和 C/C++ 环境。

在用 Ruby 编写第一个小项目时,我不知何故习惯于让所有局部变量都以下划线开头。我想我这样做的主要动机是更好的可读性和与方法调用的区别。

原则上只有三种类型的变量($global@instancelocal),绝大多数变量都以下划线开头。我不确定,这是好是坏。此外,在许多其他语言中,下划线会被替换为其他字符。

除了通常的 CamelCase 和/或 下划线分隔 之外,是否还有关于变量命名的最佳实践?专业“红宝石师”的习惯是什么?当我选择前导下划线时,我是否忽略了一些通用的 Ruby 约定?


编辑
感谢所有的答案和建议。这对我帮助很大。


下面的答案和评论的简短摘要
(针对短期访问者)

前导下划线与:

  • 方法参数:def my_method(_my_arg)
  • 块参数:例如my_array.each { |_x| puts _x}

所有其他没有前导下划线的局部变量,例如来自例如JavaScript might get confused 关于变量的预期行为。

对于变量名称和方法调用之间的视觉分离,在所有方法调用中强制自己使用“(”括号“)”可能会显着提高可读性。

【问题讨论】:

  • 其他语言的功能并不重要,因为每种语言都有不同的编码风格。我有时在块参数中使用带有前导下划线的变量,即{ |_a,_b| ... },当我想让它们脱颖而出时,或者它使代码更具可读性。随着时间的推移,可读性会转化为可维护性,因此请选择最容易进出您眼睛的内容。
  • @theTinman:带有块参数的下划线似乎也是一种合理的方式。谢谢你。
  • @Torbjoern 感谢您的简短总结,非常有帮助
  • 吉姆·斯图尔特的答案是 ruby​​ 中变量的公认答案,它是用 AirBnB 的 github.com/airbnb/ruby#throwaway-variables 等风格指南编写的

标签: ruby naming-conventions


【解决方案1】:

这个问题的现有答案现在已经有几年历史了,而且惯例已经改变。您应该只使用前导下划线 (_some_param) 或独立下划线 (_) 来表示您不关心该值。如果您分配一个变量但不使用它,rubocop 风格的 linting 工具会抱怨“无用的分配”,但它会忽略带有前导下划线的变量。这使您可以明确表示您不关心该值并且不打算使用它。

这是一个在 RSpec 上下文中有点做作的示例用例:

describe 'login' do
  let(:user) { FactoryGirl.create(:user, login: 'bob') }
  it 'must be unique' do
    _user1 = user
    user2 = User.new login: 'bob'
    expect(user2.valid?).to be_false
  end
end

这里我们表明我们的 user 助手有副作用并返回一些东西,但我们并不关心它。你也可以完全跳过这个任务,但是在一行上看到一个空的user 看起来很奇怪,并且不能清楚地表明意图:

describe 'login' do
  let(:user) { FactoryGirl.create(:user, login: 'bob') }
  it 'must be unique' do
    user
    user2 = User.new login: 'bob'
    expect(user2.valid?).to be_false
  end
end

其他场景包括忽略迭代器中的值,或覆盖您希望保留原始方法签名但不关心某些值的方法:

def greet(name, _title)
  puts "Hi, #{name}!"
end

【讨论】:

  • 最常见的方式是通过Array并行分配。例如如果一个函数“返回 3 个值”(实际上是一个长度为 3 的 Array)并且您不打算使用第一个变量,您将编写:_, myvar2, myvar3 = three_arg_function_myvar1, myvar2, myvar3 = three_arg_function
【解决方案2】:

你的想法没有错。但是,如果我无法区分本地变量和方法调用,我可能会强迫自己始终在方法上使用()。 (我的工作团队已经讨论过将这部分作为我们的编码标准)。

a = thing # var
b = thing() # method

这样做的可能优势是对其他人的可读性。有人可能会对你的前导 _ 感到疑惑,但是在所有方法调用中使用 () 应该是每个人都清楚的。

【讨论】:

  • 你是只在不带参数的方法调用中使用方括号,还是在all方法调用中不管传递多少参数?
  • 使用 all 方法调用。 (其实我自己没那么严格,但是工作中有些人在推动,我认为在上述情况下可能会有所帮助。)
  • 但是整个代码不是很快就变得太拥挤了吗?但这导致了关于引入空白的另一次讨论,并且没有一个解决方案。
  • 这不适用于super,因为supersuper() 是两个不同的东西。
  • 使用() 调用函数不是红宝石,实际上在AirBnB 的github.com/airbnb/ruby#no-args-parens 等一些风格指南中是禁止的
【解决方案3】:

根据我的经验,Ruby 中带有下划线前缀的变量很像 JavaScript 中带有下划线前缀的变量:一个“请勿触摸”标志。更具体地说,当实现者正在做的事情实际上不应该被理解为对象的一部分,或者不应该被认为是对象的概念接口时,就会使用它们。

这在 JavaScript 世界中更清楚,有人通过在变量前加下划线来模拟“私有”。它们正在编码对象的一部分在引擎盖下,从外部查看对象时可以忽略。

在 Ruby 中,我只在诸如缓存或单例实例之类的东西中真正看到过这种情况——对象的消费者应该不可见的那种东西。非下划线变量是使用您的对象的人可能有兴趣知道的东西。

无论如何,它们似乎相当罕见,我会避免使用它们,除非你想向即将到来的下一个人发出信号,表明正在发生一些额外的魔法或巫术。

就方法调用的区别而言,如果您担心方法和局部变量之间可能存在混淆,我会在self 上调用该方法来澄清。例如:

def foo
    ...
end

def some_method
    foo # method
    bar # variable
end

如果这似乎出于某种原因不清楚,您可以澄清

def some_method
    self.foo
    bar
end

【讨论】:

  • 谢谢。但我想,使用self. 进行方法调用会让我自己感到困惑。 def self.foo 不是类(不是实例)方法的定义吗?由于我很少使用,self. 会引起我的注意,让我觉得我遇到了类方法定义。
  • 是的,但关键是要了解self 的作用域。请记住,在 Ruby 中,一切都是对象 - 例如,在 C++ 中,没有类对象之类的东西,只有从类定义构建的实例。实际上,在运行时,没有类,只是围绕一组公共数据的方法集合。在 ruby​​ 中,甚至类也是实例。当你定义类时,self 是这个 class 实例,而当你在一个方法中时,self 是那个类的 instance。我知道这很令人困惑 - 很难解释......
  • 不知道是不是很难解释。对我来说,这是相当合乎逻辑和结果的,因为它是在 Ruby 中完成的。无论如何,感谢您尝试阐明它:)
【解决方案4】:

看到实例变量前面有@符号,而全局变量前面已经有$符号在ruby中,可能没有必要在变量前面放置下划线字符名字。话虽如此,我认为这不一定是一种不好的做法。如果它可以帮助您在 Ruby 中读取或编写代码,那么您应该使用它。

我有时会看到 Ruby 代码,其中类的实例方法的参数前面有一个下划线。如:

def my_method(_argument1)
  # do something
end

而且我认为,当您处理可能具有自己属性的类时,例如 rails 中的模型文件,这可能会有所帮助,以便您知道您正在处理已传递到的变量方法而不是属于类/模型的属性之一。

【讨论】:

  • 仅在方法参数中使用下划线,似乎是件好事。感谢分享。
  • 如果你的方法太长以至于参数列表在屏幕上不再可见,你不应该用下划线溅它们,你应该把它们重构得更小。
  • @JörgWMittag - 如果您阅读了我的描述,我并没有说明方法的长度或参数的数量,我只是说在编写类实例方法以区分这些时会有所帮助变量是方法的参数,而不是类本身的方法或属性。同样,这不是必需的,但有时会有所帮助。
猜你喜欢
  • 2014-12-23
  • 2013-10-13
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
相关资源
最近更新 更多