python 변수의 scope는 컴파일 타임에 정해진다

HiroPark·2023년 3월 5일
0

Jungle

목록 보기
2/10

파이썬 변수의 Scope는 컴파일 타임에 결정된다

백준 카드놓기
를 풀면서 함수 내부 변수의 Scope가 어떻게 결정되는지 알게된 것을 기록합니다.

import sys
input = sys.stdin.readline

n = int(input())
k = int(input())

cards = [input().rstrip() for _ in range(n)]

dp = [1] * n
res = set()
curStr = ""

def concat(cards, k, n, count):
    print(dp)
    print(curStr)

    if (count == k):
        res.add(curStr)
        return
    
    for i in range(n):
        if dp[i] == 0:
            curStr += cards[i]
            dp[i] = 1
        
            concat(cards,  k, n , count + 1)

            dp[i] = 0
            curStr = curStr[:-len(cards[i])] 


concat(cards, k, n, 0)
print(len(res))

결론부터 말하면, 해당코드에서 concat함수안의

  • dp : 전역변수 사용
  • curStr : 지역변수 사용
    이기에 print(curStr)구문은

    UnboundLocalError: cannot access local variable 'curStr'

해당 에러를 띄우게 됩니다.
두 변수 모두 global 키워드 없이 사용되고 있지만, 변수의 스코프가 다릅니다.

원리는 다음과 같습니다

  • 함수 안에서 어떠한 전역 변수이든지 이름을 사용하여 "읽을" 수 있습니다
  • 그러나 해당 이름에 "새로운 무언가를 할당"하려면 global 키워드를 사용해야 합니다.
  • 만약 해당 변수 명에 global 키워드를 사용하지 않고 값을 바인딩 해준다면 해당 변수는 새로운 "지역 변수"가 됩니다
  • 그리고 무언가를 할당하는지는 파이썬 코드를 실행하기 전 "컴파일타임" 에 결정됩니다.
  • 결론적으로 함수 내의 변수의 범위는 nonlocal과 global을 사용하지 않았을시, 함수 전체에서, 순서와 상관없이 이름에 대한 "binding"을 변경시켜주었는가, 에 따라 결정되는 것입니다.

그래서 curStr의 경우
" curStr += cards[i] " 과
" curStr = curStr[:-len(cards[i])] " 로
을 통해 새로운 값을 바인딩하고 있기에, 컴파일 타임에 함수 내의 지역변수로 스코프가 정해지는 것입니다. (파이썬의 str은 immutable하기 때문에, +=나 슬라이싱을 할 시, 기존의 문자열이 아닌 "아예 새로운" 문자열이 할당됨)

dp배열의 경우에도

  • dp[i] = 1
    로 값을 변경하고 있지 않나요?? 라고 생각했습니다.

그러나 파이썬의 list는 mutable합니다. 리스트 안의 값들을 바꿔준다고 해서, dp라는 변수명에 연결된 리스트 값에 대한 바인딩이 변화하지는 않는 것입니다.
그래서 기존에 존재하는 리스트에 대해 indexing이나 슬라이싱을 할 경우 global 변수로 범위가 잡힙니다.

파이썬은 interpreted language 이기 때문에 순서상 바인딩을 바꾸는 코드 앞에 있는 print(curStr)은 예외를 띄우지 말아야 하는거 아닌가요??

  • 파이썬은 interpereted 이면서 compile 언어이기도 합니다.
    우리가 파이썬코드를 작성하여 실행하면 다음의 과정을 거칩니다.
  1. 파이썬 소스코드
  2. 를 컴파일하여 바이트코드 생성
  3. 를 PVM(JVM을 생각하면 됨)이 interpret하여 기계어로 변경

PVM을 사용하는것은 JVM과 마찬가지로, "Portability" - 즉 platform independence를 가져 포팅을 용이하게 하기 위함이라 합니다.

하지만 우리가 파이썬을 사용할때 자바와 달리 명시적으로 컴파일 과정을 거치지 않고, 그냥 python program.py 로 파일을 실행하기 때문에 이러한 과정에 대해 익숙치 않은것이라 합니다.
해당 영상 을 참고했습니다..

따라서 여기서 컴파일 타임이라 하면, 소스코드를 바이트코드로 바꾸는 과정에서 변수의 범위를 미리 체크하여 설정해주고, 이를 line by line으로 interpret하는 것은 PVM이 담당하겠죠.

https://stackoverflow.com/questions/75639827/do-different-types-of-python-global-variables-have-different-levels-of-access-wi?noredirect=1#comment133445571_75639827
를 참고했습니다.

그 외에도 오늘 set의 내부 요소들은 immutable 해야하기 때문에 리스트나 딕셔너리가 들어갈 수 없다는 점.(set 자체는 mutable함)

  • set은 값들의 고유성을 판단하기 위해 해시값을 사용하는데 mutable할 경우 해시값이 바뀔 수 있기 때문.

둘다 mutable하기 때문에 list안에 dictionary를 넣고, 해당 딕셔너리와 바인딩 된 변수명을 통해 딕셔너리의 value값을 변경해주면 array안의 값도 바뀐다는점(생각해보면 당연한데..)

점도 알게 됐습니다.. 하루하루 배우는게 많네요

profile
https://de-vlog.tistory.com/ 이사중입니다

0개의 댓글