web-dev-qa-db-ja.com

外側のスコープで定義されたシャドウイング名はどれほど悪いですか?

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)
165
Framester

上記のスニペットでは大したことではありませんが、さらにいくつかの引数とかなり多くのコード行を持つ関数を想像してください。次に、data引数の名前をyaddaに変更しますが、関数の本体で使用されている場所の1つが欠落しています... dataはグローバルを参照し、奇妙になり始めます振る舞い-グローバル名NameErrorを持っていなかった場合、はるかにわかりやすいdataがあります。

また、Pythonではすべてがオブジェクト(モジュール、クラス、および関数を含む)であるため、関数、モジュール、またはクラスには明確な名前空間がないことを忘れないでください。別のシナリオは、モジュールの最上部で関数fooをインポートし、関数本体のどこかで使用することです。次に、関数に新しい引数を追加し、名前を付けます-不運-foo

最後に、組み込みの関数と型も同じ名前空間に存在し、同じ方法でシャドウイングできます。

短い関数、適切な名前付け、まともな単体テストのカバレッジがある場合、これは問題になりませんが、完璧でないコードを維持する必要があり、そのような可能性のある問題について警告されると役立つ場合があります。

177

現在最も多く投票され受け入れられている回答 で、ほとんどの回答はポイントを逃しています。

関数の長さや、変数に記述的に名前を付ける方法は問題ではありません(名前の衝突の可能性を最小限に抑えるためです)。

関数のローカル変数またはそのパラメーターがグローバルスコープ内で名前を共有するという事実は、まったく無関係です。実際、ローカル変数名をどれだけ慎重に選択しても、関数は「将来、私のクールな名前yaddaもグローバル変数として使用されるかどうか」を予測できません。ソリューション?心配しないでください! 正しい考え方は、署名のパラメーターからの入力のみを使用するように関数を設計することです、そのように何を気にする必要はありません(または、グローバルスコープ内にあり、シャドウイングはまったく問題になりません。

言い換えれば、シャドーイングの問題は、関数が同じ名前のローカル変数とグローバル変数を使用する必要がある場合にのみ問題になります。ただし、そもそもこのような設計は避けてください。 OPのコードには、実際にはそのような設計上の問題はありません。 PyCharmが十分に賢くなく、念のため警告を発するだけです。したがって、PyCharmを幸せにし、コードをきれいにするために、 silyevskの答え から引用したこのソリューションを参照して、グローバル変数を完全に削除してください。

def print_data(data):
    print data

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

main()

これは、現在のローカル機能を調整するのではなく、グローバルなものを修正/削除することにより、この問題を「解決」する適切な方法です。

101
RayLuo

場合によっては、vars +コードを別の関数に移動することをお勧めします。

def print_data(data):
    print data

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

main()
18
silyevsk

これを行う:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()
6
dR1pT

関数の長さによって異なります。関数が長いほど、将来誰かがそれを変更する可能性が高くなり、それがグローバルを意味すると考えるdataを書くようになります。実際にはローカルを意味しますが、関数が非常に長いため、その名前のローカルが存在することは彼らには明らかではありません。

関数の例では、グローバルをシャドウイングすることはまったく悪くないと思います。

5
Steve Jessop
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
JoeC

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_

そして./tests/test_Twitter2.pyのようなTwitterフィクスチャを削除します

# ./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、そしてみんなを幸せにするでしょう

2
Andrei.Danciuc