Python 강좌 / 변수의 범위와 변경 가능한 객체의 동작 이해하기
변수의 범위는 이름을 어디에서 찾을지 정하고, 객체의 변경 가능성은 같은 값을 가리키는 이름 사이에 어떤 변화가 보일지 정합니다. 대입과 객체 변경을 구분하면 함수 호출 뒤 데이터가 뜻밖에 바뀌는 원인을 이해할 수 있습니다.
지역 이름과 외부 이름
함수 안에서 대입하는 이름은 별도 선언이 없다면 지역 변수로 취급됩니다. 함수에서 외부 값을 읽는 것과 외부 이름에 새 값을 대입하는 것은 다릅니다. 전역 변수에 의존하기보다 필요한 값을 인수로 받고 결과를 반환하면 테스트하기 쉽습니다.
관련 동작과 지원 조건은 변수의 범위와 변경 가능한 객체의 동작 이해하기 관련 공식 문서에서 확인할 수 있습니다.
global은 모듈 수준 이름, nonlocal은 바깥 함수의 이름 연결을 바꿀 때 사용합니다. 모든 상태 문제를 이 선언으로 해결하려 하지 말고 데이터 전달 구조를 먼저 검토합니다.
전체 예제와 실행 결과
별도의 실습 폴더에서 아래 파일을 UTF-8로 저장합니다. 가상환경을 선택한 터미널에서 python scope.py 명령으로 실행합니다. macOS·Ubuntu에서 가상환경을 사용하지 않는 경우 명령 이름을 python3으로 바꿉니다.
scope.py 파일의 전체 내용입니다.
def add_fee(items, fee):
items.append(fee)
original = [3500, 8000]
alias = original
copy = original.copy()
add_fee(alias, 500)
print(original)
print(copy)
nested = [[1], [2]]
shallow = nested.copy()
shallow[0].append(9)
print(nested)
예상 결과는 다음과 같습니다.
[3500, 8000, 500] [3500, 8000] [[1, 9], [2]]
대입·얕은 복사·깊은 복사
alias = original은 같은 리스트를 가리키는 이름을 하나 더 만듭니다. copy()는 바깥 리스트를 새로 만들지만 내부 항목의 객체는 공유하는 얕은 복사입니다. 중첩 예제에서 안쪽 리스트가 같이 바뀌는 이유입니다.
copy.deepcopy는 내부 구조를 재귀적으로 복사하는 데 사용할 수 있으나 파일·연결·공유가 필요한 객체까지 무조건 독립 복제하는 만능 해결책은 아닙니다. 변경해야 하는 범위만 새로 구성하거나 불변 데이터 구조를 선택하는 편이 계약을 명확히 할 수 있습니다.
함수 계약에 변경 여부 드러내기
함수가 입력 리스트를 바꾸는지 문서와 이름에서 알 수 있게 합니다. 원본을 유지하는 함수라면 새 리스트를 반환하는 구현으로 바꿀 수 있습니다. 결과와 부수 효과를 모두 시험하지 않으면 반환값이 맞아도 원본 손상을 놓칠 수 있습니다.
- add_fee(copy, 100)을 호출해 original에는 100이 추가되지 않는지 확인합니다.
- 함수 안의 append를 items = items + [fee]로 바꾸면 외부 리스트가 그대로인지 확인합니다.
- 원래 코드로 되돌리고 바깥 객체와 내부 객체의 공유를 각각 설명합니다.










