쉐도우 이름은 외부 범위에서 얼마나 나쁜가?


208

방금 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)

1
또한 "This inspection detects ..."라는 문자열을 검색했지만 pycharm 온라인 도움말에서 아무것도 발견하지 못했습니다. jetbrains.com/pycharm/webhelp/getting-help.html
Framester

1
PyCharm에서이 메시지를 끄려면 <Ctrl> + <Alt> + s (설정), 편집기 , 검사 , " 외부 범위의 섀도 잉 이름 " 체크를 해제하십시오.
ChaimG

답변:


222

위의 스 니펫에는 별다른 문제가 없지만 몇 가지 인수와 코드 줄이 더 많은 함수를 상상해보십시오. 그런 다음 data인수의 이름을 바꾸려고 yadda하지만 함수 본문에서 사용되는 위치 중 하나를 그리워합니다 ... 이제 data전역을 참조하고 이상한 행동을 시작합니다- NameError그렇지 않은 경우 훨씬 더 분명한 위치 글로벌 이름이 data있습니다.

또한 파이썬에서는 모든 것이 객체 (모듈, 클래스 및 함수 포함)이므로 함수, 모듈 또는 클래스에 대한 명확한 네임 스페이스가 없다는 것을 기억하십시오. 또 다른 시나리오는 foo모듈 상단에서 함수를 가져 와서 함수 본문 어딘가에서 사용하는 것입니다. 그런 다음 함수에 새로운 인수를 추가하고 이름을 "불운"으로 지정했습니다 foo.

마지막으로 내장 함수와 형식도 같은 네임 스페이스에 존재하며 같은 방식으로 음영 처리 될 수 있습니다.

함수가 짧고, 이름이 좋고, 적절한 단위 테스트 범위가 있다면이 중 어느 것도 문제가되지 않지만, 때로는 완벽한 코드보다 적게 유지하고 그러한 가능한 문제에 대해 경고를받는 것이 도움이 될 수 있습니다.


21
운 좋게도 PyCharm (OP에서 사용)은 이름이 매우 좋은 이름 바꾸기 작업을 통해 동일한 범위에서 사용되는 모든 곳에서 변수의 이름을 바꾸므로 이름 바꾸기 오류가 덜 발생합니다.
wojtow

PyCharm의 이름 바꾸기 작업 외에도 외부 범위를 참조하는 변수에 대한 특수 구문 강조 표시를 원합니다. 이 두 가지는이 시간이 걸리는 그림자 해상도 게임과 관련이 없어야합니다.
Leo

참고 : nonlocal키워드를 사용하여 (마감과 같은) 외부 점수를 명시 적으로 지정할 수 있습니다 . 변수는 외부에서 명시 적으로 그림자를 나타내지 않기 때문에 그림자와 다릅니다.
Felix D.

149

현재 가장 많이지지되고 승인 된 답변 과 여기에있는 대부분의 답변이 요점을 놓칩니다.

함수의 길이 또는 변수의 이름을 설명 적으로 지정하는 것은 중요하지 않습니다 (이름 충돌 가능성을 최소화하기 위해).

함수의 지역 변수 또는 매개 변수가 전역 범위에서 이름을 공유한다는 사실은 전혀 관련이 없습니다. 사실, 지역 변수 이름을 아무리 신중하게 선택하더라도 함수는 "내 멋진 이름인지 여부를 예측할 수 없습니다"yadda 이 나중에 전역 변수로 사용 . 해결책? 그것에 대해 걱정하지 마십시오! 올바른 사고 방식은 signature의 매개 변수에서만 입력을 사용하도록 함수를 설계 하는 것입니다. 그러면 전역 범위의 내용을 신경 쓸 필요가 없으며 섀도 잉은 전혀 문제가되지 않습니다.

즉, 섀도 잉 문제는 함수가 동일한 이름의 로컬 변수와 전역 변수를 사용해야하는 경우에만 중요합니다. 그러나 처음에는 그러한 디자인을 피해야합니다. OP의 코드에는 실제로 그러한 디자인 문제가 없습니다. PyCharm이 충분히 똑똑하지 않고 단지 경우를 대비하여 경고를합니다. 따라서 PyCharm을 행복하게 만들고 코드를 깨끗하게 만들려면 silyevsk의 답변 에서 인용 한이 솔루션을 참조하십시오 려면 전역 변수를 완전히 제거하는 .

def print_data(data):
    print data

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

main()

이것은 현재 로컬 기능을 조정하지 않고 전역을 수정 / 제거하여이 문제를 "해결"하는 올바른 방법입니다.


11
글쎄, 완벽한 세상에서, 당신은 오타를 만들거나 매개 변수를 변경할 때 검색 대치 중 하나를 잊어 버리지 만 실수는 발생하고 PyCharm이 말하는 것입니다- "경고-기술적으로 오류가 없습니다. 쉽게 문제가 될 수 있습니다 "
dwanderson

1
@ dwanderson 언급 한 상황은 새로운 것이 아니며 현재 선택된 답변에 명확하게 설명되어 있습니다. 그러나 내가하려고하는 요점은 전역 변수를 피하지 말고 전역 변수를 피해야한다는 것입니다. 후자는 요점을 놓친다. 알 겠어요? 알았다?
RayLuo

4
함수가 가능한 한 "순수"해야한다는 사실에 전적으로 동의하지만 두 가지 중요한 점을 완전히 놓치게됩니다. 로컬로 정의되지 않은 경우 파이썬이 둘러싸는 범위에서 이름을 찾지 못하도록 제한 할 방법이 없으며 모든 것이 (모듈) , 함수, 클래스 등)는 객체이며 다른 "변수"와 동일한 네임 스페이스에 있습니다. 위의 스 니펫에서 print_dataIS는 전역 변수입니다. 생각해보십시오.
bruno desthuilliers

2
함수에 정의 된 함수를 사용하고 있기 때문에이 스레드에서 결국 전역 네임 스페이스를 어지럽히거나 별도의 파일을 사용하지 않고 외부 함수를 더 읽기 쉽게 만들었습니다. 이 예제는 로컬이 아닌 비전 역 변수가 음영 처리되는 일반적인 경우에는 적용되지 않습니다.
micseydel

2
동의하다. 여기서 문제는 파이썬 범위입니다. 현재 범위 밖의 객체에 대한 명시 적 액세스가 문제를 요구하고 있습니다. 누가 그것을 원할까요! 그렇지 않으면 파이썬은 꽤 잘 생각되는 언어이기 때문에 부끄러운 일입니다 (모듈 명명에서 비슷한 모호성을 견딜 수 없음).
CodeCabbie

24

경우에 따라 좋은 해결 방법은 vars + 코드를 다른 함수로 옮기는 것입니다.

def print_data(data):
    print data

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

main()

예. 좋은 아이디어는 리팩토링으로 지역 변수와 전역 변수를 처리 할 수 ​​있다고 생각합니다. 귀하의 팁은 원시적 아이디어에 대한 이러한 잠재적 위험을 제거하는 데 실제로 도움이됩니다
stanleyxu2005

5

기능의 길이에 따라 다릅니다. 함수가 길수록 미래에 누군가 수정 data하면 글로벌을 의미한다고 생각할 가능성이 높아집니다 . 사실 그것은 로컬을 의미하지만 함수가 너무 길기 때문에 그 이름을 가진 로컬이 존재한다는 것은 분명하지 않습니다.

예제 함수의 경우 전역 그림자를 만드는 것이 전혀 나쁘지 않다고 생각합니다.


5

이 작업을 수행:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

3
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)

47
나는 혼란스럽지 않다. 그것은 분명히 매개 변수입니다.

2
@delnan이 간단한 예제에서는 혼동하지 않을 수 있지만 근처에 정의 된 다른 함수가 전역 data코드를 수백 줄의 코드로 사용한다면 어떻게 될까요?
존 콜란 두 오니

13
@HevyLight 근처의 다른 기능을 볼 필요가 없습니다. 나는 단지이 기능을보고이 볼 수 data있는 지역의 이름 그래서도 / 검사 같은 이름의 전역 여부를 기억 귀찮게하지 않습니다, 기능 존재 , 그것이 포함 된 내용을 혼자 할 수 있습니다.

4
나는이 추론이 유효하다고 생각하지 않습니다. 단지 전역을 사용하기 때문에 함수 내에서 "전역 데이터"를 정의해야합니다. 그렇지 않으면 전역에 액세스 할 수 없습니다.
CodyF

1
@CodyF False- 당신이 정의하지만 단지 사용하려고하지 않는 경우 data가 하나 찾을 때까지 그렇게, 그것은, 범위를 통해 조회 하지 글로벌 찾을 수 있습니다 data. data = [1, 2, 3]; def foo(): print(data); foo()
dwanderson

3

나는 pycharm의 오른쪽 상단에 녹색 틱을보고 싶습니다. 중요한 경고에 집중할 수 있도록이 경고를 지우려면 변수 이름에 밑줄을 추가하십시오.

data = [4, 5, 6]

def print_data(data_): 
    print(data_)

print_data(data)

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 및 모든 사람을 행복하게 할 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.