Python의 블록 범위


93

다른 언어로 코딩 할 때 다음과 같이 블록 범위를 만드는 경우가 있습니다.

statement
...
statement
{
    statement
    ...
    statement
}
statement
...
statement

한 가지 목적은 코드 가독성을 높이는 것입니다. 특정 명령문이 논리 단위를 형성하거나 특정 지역 변수가 해당 블록에서만 사용된다는 것을 보여주기위한 것입니다.

파이썬에서 똑같은 일을하는 관용적 인 방법이 있습니까?


2
One purpose (of many) is to improve code readability-올바르게 작성된 (즉, python을 따르는) Python 코드 는 이러한 장식을 읽을 필요가 없습니다. 사실, 파이썬에 대해 내가 좋아하는 (많은) 것 중 하나입니다.
Burhan Khalid

나는 __exit__with진술 을 가지고 놀려고 노력 globals()했지만 실패했습니다.
Ruggero Turra 2015 년

1
자원 획득과 관련된 가변 수명을 정의하는 것이 매우 유용 할 것입니다.
Ruggero Turra

25
@BurhanKhalid : 사실이 아닙니다. 파이썬은 여기저기서 임시 변수로 로컬 범위를 오염시키는 것을 막지 않습니다. 당신이 설정 한 경우 모든 사용을 즉시 호출되는 중첩 된 함수를 정의하는 예에 하나의 임시 변수의를, 파이썬의 선 중 하나 행복하지 않을 것입니다. 변수의 범위를 명시 적으로 제한하는 것은 가독성을 높이는 도구입니다. "이 식별자는 아래에 사용됩니까?" -가장 우아한 파이썬 코드를 읽을 때도 발생할 수있는 질문입니다.
bluenote10

18
@BurhanKhalid 기능이 없어도 괜찮습니다. 그러나 그것을 "선"이라고 부르는 것은 단지 역겨운 일입니다.
Phil

답변:


81

아니요, 블록 범위 생성에 대한 언어 지원은 없습니다.

다음 구성은 범위를 만듭니다.

  • 기준 치수
  • 수업
  • 함수 (람다 포함)
  • 발전기 표현
  • 이해력 (dict, set, list (in Python 3.x))

38

파이썬의 관용적 인 방법은 함수를 짧게 유지하는 것입니다. 이것이 필요하다고 생각되면 코드를 리팩토링하십시오! :)

Python은 각 모듈, 클래스, 함수, 생성기 표현식, dict comprehension, set comprehension 및 Python 3.x에서 각 목록 이해에 대한 새 범위를 만듭니다. 이 외에도 함수 내에 중첩 된 범위가 없습니다.


12
"프로그래밍에서 가장 중요한 것은 이름을 부여하는 능력입니다. 두 번째로 중요한 것은 이름을 부여 할 필요가 없다는 것입니다." 대부분의 경우 Python은 범위 (변수 등)에 이름을 지정해야합니다. 이 점에서 파이썬 변수는 두 번째로 중요한 테스트입니다.
Krazy Glew

19
프로그래밍에서 가장 중요한 것은 애플리케이션의 종속성을 관리하고 코드 블록의 범위를 관리하는 기능입니다. 익명 블록을 사용하면 콜백의 수명을 제한 할 수 있습니다. 그렇지 않으면 콜백이 한 번만 사용되지만 프로그램 기간 동안 유지되므로 전역 범위가 복잡해지고 코드 가독성이 저하됩니다.
Dmitry

방금 변수가 dict / set 이해력에 로컬이라는 것을 알았습니다. Python 2.7과 3.3을 사용해 보았지만 버전에 따라 달라지는 지 잘 모르겠습니다.
wjandrea

1
@wjandrea 당신이 맞아요 – 목록에 추가되었습니다. 이들에 대한 Python 버전간에 차이가 없어야합니다.
Sven Marnach

4
함수 내에서 함수를 아주 잘 만들 수 있으므로 마지막 문장을 다시 말하겠습니다. 따라서 함수 내에 중첩 된 범위가 있습니다.
ThomasH

18

함수 내에서 함수를 선언 한 다음 즉시 호출하여 Python의 C ++ 블록 범위와 유사한 작업을 수행 할 수 있습니다. 예를 들면 :

def my_func():
    shared_variable = calculate_thing()

    def do_first_thing():
        ... = shared_variable
    do_first_thing()

    def do_second_thing():
        foo(shared_variable)
        ...
    do_second_thing()

왜 이것을하고 싶은지 잘 모르겠다면 이 비디오 가 당신을 설득 할 것입니다.

기본 원칙은 '쓰레기'(추가 유형 / 함수)를 절대적으로 필요한 것보다 더 넓은 범위에 도입하지 않고 가능한 한 엄격하게 모든 범위를 do_first_thing()지정하는 것입니다. 호출 기능.


또한, 예를 들어 볼 때, TensorFlow 자습서 구글 개발자가 사용하고있는 방법입니다 여기에
니노 Filiu

13

블록 범위가 없다는 데 동의합니다. 그러나 파이썬 3의 한 곳은 마치 블록 범위가있는 것처럼 SEEM을 만듭니다.

이 모습을 준 무슨 일이 있었습니까? 이것은 파이썬 2에서 제대로 작동했지만 파이썬 3에서 변수 누출을 ​​막기 위해이 트릭을 수행했으며이 변경으로 인해 여기에 블록 범위가있는 것처럼 보입니다.

설명하겠습니다.


범위의 개념에 따라 동일한 범위 내에 동일한 이름을 가진 변수를 도입 할 때 해당 값을 수정해야합니다.

이것은 파이썬 2에서 일어나는 일입니다.

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'W'

그러나 파이썬 3에서는 같은 이름을 가진 변수가 도입되었지만 재정의하지 않지만 목록 이해는 어떤 이유로 샌드 박스처럼 작동하며 새 범위를 만드는 것처럼 보입니다.

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'OLD'

이 대답은 응답자 @Thomas의 진술에 위배 됩니다. 범위를 만드는 유일한 방법은 함수, 클래스 또는 모듈 입니다. 이것은 새 범위를 만드는 다른 장소처럼 보이기 때문입니다.


0

모듈 (및 패키지)은 프로그램을 별도의 네임 스페이스로 나누는 훌륭한 Python 방식이며,이 질문의 암묵적인 목표 인 것 같습니다. 사실, 파이썬의 기초를 배우면서 블록 스코프 기능이 없다는 사실에 실망했습니다. 그러나 일단 파이썬 모듈을 이해하면 블록 범위 없이도 이전 목표를 더 우아하게 실현할 수있었습니다.

동기를 부여하고 사람들을 올바른 방향으로 안내하기 위해 Python의 범위 지정 구문에 대한 명시적인 예를 제공하는 것이 유용하다고 생각합니다. 먼저 Python 클래스를 사용하여 블록 범위를 구현하는 데 실패한 시도를 설명합니다. 다음으로 파이썬 모듈을 사용하여 더 유용한 것을 어떻게 얻었는지 설명합니다. 마지막으로 데이터로드 및 필터링에 패키지를 실제로 적용하는 방법에 대해 설명합니다.

클래스로 블록 범위 시도

잠시 동안 클래스 선언 내부에 코드를 붙여서 블록 범위를 달성했다고 생각했습니다.

x = 5
class BlockScopeAttempt:
    x = 10
    print(x) # Output: 10
print(x) # Output: 5

불행히도 이것은 함수가 정의되면 분해됩니다.

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(x) 
    printx2() # Output: 5!!!

클래스 내에 정의 된 함수는 전역 범위를 사용하기 때문입니다. 이 문제를 해결하는 가장 쉬운 (유일한 것은 아니지만) 방법은 클래스를 명시 적으로 지정하는 것입니다.

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(BlockScopeAttempt.x)  # Added class name
    printx2() # Output: 10

클래스에 포함되어 있는지 여부에 따라 함수를 다르게 작성해야하기 때문에 이것은 그렇게 우아하지 않습니다.

Python 모듈로 더 나은 결과

모듈은 정적 클래스와 매우 유사하지만 모듈은 내 경험에서 훨씬 더 깔끔합니다. 모듈로 동일한 작업을 수행하기 my_module.py위해 다음 내용으로 현재 작업 디렉토리에 라는 파일을 만듭니다 .

x = 10
print(x) # (A)

def printx():
    global x
    print(x) # (B)

그런 다음 주 파일 또는 대화 형 (예 : Jupyter) 세션에서

x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5

설명으로 각 Python 파일은 자체 전역 네임 스페이스를 가진 모듈을 정의합니다. 모듈을 가져 오면 .구문 을 사용하여이 네임 스페이스의 변수에 액세스 할 수 있습니다 .

대화식 세션에서 모듈로 작업하는 경우 처음에이 두 줄을 실행할 수 있습니다.

%load_ext autoreload
%autoreload 2

모듈은 해당 파일이 수정되면 자동으로 다시로드됩니다.

데이터로드 및 필터링을위한 패키지

패키지의 개념은 모듈 개념을 약간 확장 한 것입니다. 패키지는 __init__.py가져올 때 실행되는 파일 (비어있을 수 있음)을 포함하는 디렉토리 입니다. 이 디렉토리 내의 모듈 / 패키지는 .구문을 사용하여 액세스 할 수 있습니다 .

데이터 분석을 위해 대용량 데이터 파일을 읽고 대화식으로 다양한 필터를 적용해야하는 경우가 많습니다. 파일을 읽는 데 몇 분이 걸리므로 한 번만하고 싶습니다. 객체 지향 프로그래밍에 대해 학교에서 배운 내용을 기반으로 클래스의 메서드로 필터링 및로드를위한 코드를 작성해야한다고 믿었습니다. 이 방법의 가장 큰 단점은 필터를 재정의하면 클래스 정의가 변경되므로 데이터를 포함한 전체 클래스를 다시로드해야한다는 것입니다.

요즘 Python에서는 및 my_data이라는 하위 모듈이 포함 된라는 패키지를 정의합니다 . 내부 에서 상대 가져 오기를 수행 할 수 있습니다.loadfilterfilter.py

from .load import raw_data

내가 수정하는 경우 filter.py, 다음 autoreload의 변화를 감지합니다. 다시로드되지 않으므로 load.py데이터를 다시로드 할 필요가 없습니다. 이렇게하면 Jupyter 노트북에서 필터링 코드의 프로토 타입을 만들고 함수로 래핑 한 다음 노트북에서 filter.py. 이 문제를 파악하는 것은 내 작업 흐름에 혁명을 일으켰고 나를 회의론자에서 "Zen of Python"의 신자로 전환했습니다.

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