外部作用域中定义的阴影名称有多糟糕?

Fra*_*ter 185 python coding-style pycharm

我刚刚切换到Pycharm,我很高兴它提供了所有警告和提示,以改进我的代码.除了这个我不明白的:

This inspection detects shadowing names defined in outer scopes.

我知道从外部作用域访问变量是不好的做法但是遮蔽外部作用域的问题是什么?

这是一个例子,Pycharm给了我警告信息:

data = [4, 5, 6]

def print_data(data): # <-- Warning: "Shadows 'data' from outer scope
    print data

print_data(data)
Run Code Online (Sandbox Code Playgroud)

bru*_*ers 200

在上面的代码片段中没什么大不了的,但想象一个带有更多参数和更多代码行的函数.然后你决定重新命名你的data论点,yadda但是错过了它在函数体中使用的一个地方......现在data指的是全局,你开始有一种奇怪的行为 - NameError如果你不这样做你将会更加明显有一个全球名称data.

还要记住,在Python中,一切都是一个对象(包括模块,类和函数),因此没有针对函数,模块或类的不同命名空间.另一种情况是您foo在模块顶部导入函数,并在函数体中的某处使用它.然后你为你的函数添加一个新的参数并命名它 - 运气不好 - foo.

最后,内置函数和类型也存在于同一名称空间中,并且可以以相同的方式进行阴影处理.

如果你有短暂的功能,良好的命名和适当的单元测试覆盖率,这些都不是一个问题,但是,有时你必须保持不完美的代码并被警告这些可能的问题可能会有所帮助.

  • 幸运的是,PyCharm(由OP使用)具有非常好的重命名操作,它将变量重命名为在同一范围内使用的所有位置,这使得重命名错误的可能性降低. (16认同)
  • 除了 PyCharm 的重命名操作之外,我还希望为引用外部作用域的变量提供特殊的语法突出显示。这两个应该使这个耗时的阴影分辨率游戏变得无关紧要。 (3认同)

Ray*_*Luo 123

目前最多投票和接受的答案和大多数答案都忽略了这一点.

无论你的函数有多长,或者你如何描述你的变量(希望最大限度地减少潜在名称冲突的可能性).

函数的局部变量或其参数碰巧在全局范围内共享名称这一事实完全无关紧要.事实上,无论你多么谨慎地选择局部变量名,你的功能都无法预见"我的名字yadda在未来是否也将被用作全局变量?".解决方案?根本不用担心!正确的思维方式是设计你的函数来消费来自签名中的参数的输入,这样你就不需要关心全局范围内的(或将会是什么),然后阴影根本不是问题.

换句话说,当您的函数需要使用相同名称的局部变量和全局变量时,阴影问题才有意义.但是你应该首先避免这种设计.OP的代码并没有真正的设计问题.只是PyCharm不够聪明,它会发出警告以防万一.因此,只是为了让PyCharm感到高兴,并使我们的代码变得干净,请参阅这个解决方案,引用silyevsk的答案来完全删除全局变量.

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()
Run Code Online (Sandbox Code Playgroud)

这是通过修复/删除全局内容而不是调整当前本地函数来"解决"此问题的正确方法.

  • 嗯,当然,在一个完美的世界里,当你改变参数时,你会犯错误,或忘记你的搜索替换之一,但是错误发生了,这就是PyCharm所说的 - "警告 - 技术上没有任何错误,但是这个很容易成为一个问题" (8认同)
  • 我完全同意函数应尽可能"纯粹",但你完全错过了两个要点:如果没有在本地定义的话,就没有办法限制Python查找封闭范围中的名称,并且_everything_(模块) ,函数,类等)是一个对象,与任何其他"变量"位于同一名称空间中.在上面的代码片段中,`print_data`是一个全局变量.想一想...... (2认同)
  • 我最终在这个线程上,因为我正在使用函数中定义的函数,使外部函数更易读,而不会使全局命名空间混乱或者使用单独的文件.这里的示例不适用于被遮蔽的非局部非全局变量的一般情况. (2认同)
  • 同意。这里的问题是 Python 作用域。对当前范围之外的对象进行非显式访问是自找麻烦。谁会想要那个!遗憾的是,否则 Python 是一种经过深思熟虑的语言(尽管模块命名也存在类似的歧义)。 (2认同)
  • @florianH,我不使用 PyCharm,也许你可以以某种方式在 main() 末尾设置一个断点? (2认同)
  • @RayLuo,你不知道你的小提示对我未来几年的 Py 编码有多大帮助。尴尬又高兴:-)。自我注意(如果有其他人的话......):**断点和调试器**在任何感兴趣的地方与代码进行实时交互;再也不用烦人地编写大量数据打印命令来检查代码中发生了什么。如果其他人无法打开Pycharm提示:连接调试器进程后,在调试器窗口的右下角找到彩色的Pycharm十字(可能首先被隐藏--&gt;单击小箭头&gt;&gt;) (2认同)

sil*_*vsk 21

在某些情况下,一个好的解决方法可能是将vars +代码移动到另一个函数:

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()
Run Code Online (Sandbox Code Playgroud)


Baz*_*Baz 8

我喜欢在 PyCharm 的右上角看到一个绿色的勾。我在变量名后面加上下划线只是为了清除这个警告,这样我就可以专注于重要的警告。

data = [4, 5, 6]

def print_data(data_):
    print(data_)

print_data(data)
Run Code Online (Sandbox Code Playgroud)

  • 忘记 _ 时容易出错 (9认同)
  • 我最初对此投了赞成票,并做了同样的事情。我现在正在将所有项目中的代码恢复为_有意_隐藏全局(当我不想要或不需要它时)。我同意@eyaler,这非常容易出错。 (4认同)
  • `# noinspection PyShadowingNames` (3认同)

小智 7

做这个:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 5

这取决于功能有多长.功能越长,将来修改它的人就越会data认为它意味着全局.事实上它意味着本地,但因为功能太长,所以对他们来说并不明显存在具有该名称的本地.

对于你的示例函数,我认为阴影全局并不坏.