外层作用域中阴影变量的名称为xyz。

37

我正在使用pycharm,它列出了与代码相关的所有错误/警告。虽然我理解其中大部分,但我不确定这个警告“Shadows name xyz from outer scope”的含义。有一些关于此问题的SO帖子:How bad is shadowing names defined in outer scopes?,但是它们似乎在访问全局变量。

在我的情况下,我的__main__函数有一些变量名,然后它调用另一个函数sample_func,该函数再次使用这些变量名(主要是循环变量名)。我假设因为我在另一个函数中,这些变量的作用域将是局部的,但是警告似乎表明情况并非如此。

你有什么想法吗?以下是一些代码供您参考:

def sample_func():
    for x in range(1, 5):  --> shadows name x from outer scope
        print x

if __name__ == "__main__":
    for x in range(1, 5):
        sample_func()

11
实际上,你并没有一个名为 __main__函数,只有一些在 __name__ == "__main__" 时才会执行的模块级别的代码。因此,并不存在 "主体部分" 的本地作用域;for 循环在模块级别执行(并绑定了 x)。 - Ben
感谢Ben的解释。 - The Wanderer
5个回答

31

这个警告是关于在内部作用域中重复使用这些名称可能会引入潜在危险。它可能导致您错过bug。例如,考虑以下情况

def sample_func(*args):
    smaple = sum(args) # note the misspelling of `sample here`
    print(sample * sample)

if __name__ == "__main__":
    for sample in range(1, 5):
        sample_func()

因为您使用了相同的名称,所以在函数内部拼写错误不会导致错误。

当您的代码非常简单时,您可以毫不费力地使用这种类型的代码而没有任何后果。但是,使用这些“最佳实践”可以避免在更复杂的代码上出现错误。


2
这是一个非常好的例子。然而,无论你尝试多少次,总会有一些变量名是相同的。你认为有什么更好的方法来限定变量名的作用域?正如Ned所建议的那样,使用另一个名为main()的函数是否可以限定变量的作用域? - The Wanderer
3
是的,通过定义一个“main”函数,他改变了原始定义的作用域,使它们不再冲突。 - krethika
1
谢谢mehtunguh,这非常有用。只有一个最后的问题?我的脚本需要一些参数作为输入。我应该在__main__中解析它们,然后将其作为参数传递给main()吗?我知道这是一个很愚蠢的问题,但还是想了解最佳实践。 - The Wanderer
这是我的偏好,也是我看到最常见的方式。 - krethika
不错的尝试,但这种情况现实吗?PEP8指南绝对是出于偏执狂。现在大多数IDE都内置了变量名拼写检查器。因此,可以安全地忽略PEP8中的这一部分。 - nehem

18

在sample_func函数内部,你的主函数if分支中的代码实际上是在作用域中的。你可以从变量x中读取(试一下)。这是可以的,因为你并不真正关心它,所以你有几个选项来继续前进。

1)禁用pycharm中的遮蔽警告。老实说,这是最直接的方法,根据你的编码经验而定,这可能是最合理的选择(如果你是相对较新的程序员,我不建议这样做)。

2)将你的主要代码放入一个主函数中。这可能是任何生产级代码的最佳解决方案。Python非常擅长按照你想要的方式完成任务,因此你应该小心不要陷入困境。如果你正在构建一个模块,那么在模块级别上有很多逻辑可能会让你遇到麻烦。相反,以下内容可能会有所帮助:

def main():
    # Note, as of python 2.7 the interpreter became smart enough
    # to realize that x is defined in a loop, so printing x on this
    # line (prior to the for loop executing) will throw an exception!
    # However, if you print x by itself without the for loop it will
    # expose that it's still in scope. See https://gist.github.com/nedrocks/fe42a4c3b5d05f1cb61e18c4dabe1e7a
    for x in range(1, 5):
        sample_func()

if __name__ == '__main__':
    main()

3)不要在更广泛的范围中使用与您正在使用的变量名称相同的变量名称。这很难强制执行,有点与#1相反。


@SurestTexas 我想我的回答不够清晰,但我指出原问题的代码中 x 在函数声明之前就在 sample_func 中可用。抱歉表达不清! - Ned Rockson
您是在说上面的OP代码已经被修改过,而不是您最初回应的那个代码吗?因为目前显示在OP代码上方的就是我用于测试的代码。我只是在OP代码的第一行和第二行之间添加了一个print(x)语句。那次尝试产生了我描述的错误。也就是说,在sample_func定义它的for循环之前,我尝试从sample_func的代码中访问外部范围的x - user4805123
你说得对!看起来解释器在那个for循环中聪明地使用了x并自动进行了遮蔽。因此,如果你在这里看到代码:https://gist.github.com/nedrocks/fe42a4c3b5d05f1cb61e18c4dabe1e7a 它可以工作,但是当你执行上面的代码(在你那里失败了)时,它会抛出一个错误。好发现! - Ned Rockson
1
因为在 sample_func 中写了变量名 x,所以它是该函数整个作用域内的局部变量;即使在本地 x 实际上没有值之前,模块全局 x 也是不可访问的。如果 x 在函数中只被读取,它将读取全局 x - Ben
...理解更加深入。谢谢! - user4805123
显示剩余3条评论

2

这只是一个警告,正如链接的问题所解释的那样,有时它可能会引起问题,但在您的情况下 x 是局部变量。您收到警告是因为 if __name__ == "__main__": 中的 x 在全局范围内。它不会对您函数中的 x 产生任何影响,所以我不会担心这个警告。


2
我知道这是一个老帖子,与提问者试图找出的问题不相关,但我正在寻找为什么PyCharm在复杂的if / elif语句块上向我显示“外部作用域的阴影名称”消息的答案...
结果发现我在函数开头大写了一些全局变量名,但在函数中间使用小写字母来编写if / elif块。
我知道这是初学者的错误,但是一旦我纠正了这个问题,PyCharm中的“Shadows name from outer scope”消息就消失了,并且变量不再显示为灰色...
所以我学到的教训是,这个PyCharm消息可能是由于变量名称中的大小写错误等简单原因引起的...
我只有在将该函数分成三个函数以查看是否可以消除“Shadows ...”错误时才意识到问题,因为我认为我存在缩进问题,这导致了问题!
这可能会帮助另一个新手,他们正在思考为什么会出现这个错误 :-)

1
我在一个名为year的方法中遇到了一个参数警告,但没有其他变量共享该名称。然后我意识到这是因为from pyspark.sql.functions import *导入了一个year变量。将其更改为仅导入我们需要的功能可以消除警告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接