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

时间:2013-11-21 15:35:11

标签: 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)

8 个答案:

答案 0 :(得分:173)

在上面的代码片段中没什么大不了的,但想象一个带有更多参数和更多代码行的函数。然后你决定将你的data参数重命名为yadda,但是错过了它在函数体中使用的其中一个位置......现在data指的是全局,你开始有点奇怪行为 - 如果您没有全局名称NameError,那么您将有更明显的data

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

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

如果你的功能很短,命名良好且单位测试覆盖率不高,这一切都不是问题,但是,有时你必须保持不完美的代码并被警告这些可能的问题可能有所帮助。

答案 1 :(得分:100)

The currently most up-voted and accepted answer并且大多数答案都错过了重点。

你的功能有多长,或者你如何描述你的变量(希望最大限度地减少潜在名称冲突的可能性)并不重要。

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

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

def print_data(data):
    print data

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

main()

这是解决问题的正确方法。这个问题,通过修复/删除全局事物,而不是调整当前的本地函数。

答案 2 :(得分:18)

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

def print_data(data):
    print data

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

main()

答案 3 :(得分:6)

这样做:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

答案 4 :(得分:5)

data = [4, 5, 6] #your global variable

def print_data(data): # <-- Pass in a parameter called "data"
    print data  # <-- Note: You can access global variable inside your function, BUT for now, which is which? the parameter or the global variable? Confused, huh?

print_data(data)

答案 5 :(得分:5)

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

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

答案 6 :(得分:3)

我喜欢在pycharm的右上角看到一个绿色的勾号。我为变量名加上下划线只是为了清除此警告,因此我可以将重点放在重要警告上。

data = [4, 5, 6]

def print_data(data_): 
    print(data_)

print_data(data)

答案 7 :(得分:2)

它看起来像是100%pytest代码模式

请参阅:

https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions

我遇到了同样的问题,这就是我发现这篇文章的原因;)

# ./tests/test_twitter1.py
import os
import pytest

from mylib import db
# ...

@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

它将以This inspection detects shadowing names defined in outer scopes.

发出警告

要解决此问题,只需将twitter灯具移至./tests/conftest.py

即可
# ./tests/conftest.py
import pytest

from syntropy import db


@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

并移除twitter

中的./tests/test_twitter2.py灯具
# ./tests/test_twitter2.py
import os
import pytest

from mylib import db
# ...

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

这将是快乐的QA,Pycharm和所有人