개요
파이썬 변수와 함수 인수 전달 방식을 정리함 주요 키워드 세 가지 정리
- call by value
- call by reference
- call by object reference (aka call by sharing)
헷갈리는 포인트는 call by reference와 call by object reference의 차이임 파이썬에서 실제로 일어나는 바인딩과 가변성의 조합으로 이해 필요
호출 방식 개념 정리
Call by Value
인수 전달 시 값의 복사 전달 함수 내부에서 인자를 변경해도 호출자 쪽 원본에는 영향 없음
Call by Reference
인수 전달 시 변수 자체를 참조로 전달하는 모델 함수 내부 변경이 호출자 변수에 그대로 반영됨 특정 언어에서만 엄밀히 지원되는 개념이며, C는 포인터를 값으로 전달해 유사 동작을 만들 수 있으나 언어 차원의 call by reference와는 구분 필요 Java는 참조 자체를 값으로 전달하므로 call by reference가 아님에 주의
Call by Object Reference (Call by Sharing)
파이썬의 인수 전달 방식 함수는 객체에 대한 참조를 값으로 전달받음
- 매개변수 이름이 같은 객체를 가리키는 바인딩을 가짐
- 매개변수를 재바인딩하면 호출자에는 영향 없음
- 전달된 객체가 mutable이면 그 객체의 내부 상태 변경은 호출자에게 보임
핵심은 참조 자체는 공유되지만, 이름 바인딩은 함수 스코프에서 독립적으로 움직인다는 점임
파이썬 객체와 가변성
파이썬에서 모든 값은 객체이고, 변수는 객체를 가리키는 이름 바인딩임 a = 1, c = 1이라면 1이라는 동일 객체에 a와 c라는 이름표가 붙은 상태
- mutable: 값 변경 가능, id() 불변 유지
- immutable: 값 변경 불가, 다른 값을 원하면 새 객체 생성 필요
- 대표 타입: 숫자, 문자열, 튜플
이 정의가 호출 방식의 체감 동작을 갈라놓는 핵심 축임
Immutable 예시
불변 객체를 함수로 전달한 뒤 내부에서 재할당이 일어나는 경우 동작
def func(x):
x = 2 # 새 객체에 대한 로컬 바인딩으로 변경
a = 1
func(a)
# 호출 후 a는 여전히 1을 가리킴설명
- 호출 시 a와 매개변수 x는 같은 객체 1을 가리킴
- 함수 내부에서 x = 2 수행 시 2라는 새 객체가 생성되고, x의 바인딩만 2로 이동
- 호출자 a는 여전히 1을 가리킴
id()로 확인 가능
def func(x):
before = id(x)
x = 2
after = id(x)
return before, after
bid_before = id(2)
a = 1
x_before, x_after = func(a)
# x_before == id(1)이고, x_after == id(2) 관계 성립요지
- 불변 객체는 내부 상태 변경 자체가 불가능
- 함수 내부에서 보이는 변화는 재바인딩일 뿐이며, 호출자 바인딩에는 영향 없음
Mutable 예시
가변 객체에서는 내부 변경이 호출자에 반영됨
def push_five(arr):
arr.append(5) # 같은 객체에 대한 in-place 변경
lst = [1, 2, 3, 4]
push_five(lst)
# 호출 후 lst는 [1, 2, 3, 4, 5]설명
- 매개변수와 호출자 변수 모두 동일 리스트 객체를 가리킴
- append는 동일 id의 객체 내부 상태 변경
- 호출자도 같은 객체를 보므로 변경이 그대로 관찰됨
반대로 매개변수 재바인딩은 호출자에 영향 없음
def replace(arr):
arr = [5, 6] # 새 리스트 객체로 로컬 바인딩 변경
lst = [1, 2, 3, 4]
replace(lst)
# 호출 후 lst는 여전히 [1, 2, 3, 4]요지
- in-place 변경은 공유된 객체 상태 변경이므로 영향 전파
- 재바인딩은 로컬 이름표 이동이므로 영향 없음
Call by Reference와의 차이 요약
- call by reference: 매개변수 이름이 호출자 변수 자체를 가리키는 모델, 재바인딩조차 호출자에 반영됨
- call by object reference: 객체 참조를 값으로 전달, in-place 변경만 공유되고 재바인딩은 지역에 한정됨
시각적으로는 비슷해 보이나 재바인딩의 전파 여부가 결정적 차이 파이썬은 후자에 해당
실무 주의와 베스트 프랙티스
- 불변 자료형 선호로 부수 효과 최소화
- 숫자, 문자열, 튜플 사용 고려
- 가변 객체 전달 시 의도한 변경 범위 명시
- 함수가 입력을 수정함을 인터페이스로 드러내거나, 복사본 사용
- 복사 전략
- 얕은 복사로 충분하면 list(x), x.copy 사용
- 중첩 구조면 deepcopy 검토, 비용 트레이드오프 인지
- 함수는 가능하면 새 값을 반환하고 입력은 그대로 두는 순수 함수 스타일 지향
- 가변 기본값 인자 금지
- def f(x=[]): 형태 지양, 기본값은 None으로 두고 내부에서 새로 생성
- 다른 언어 배경에서 온 혼동 주의
- 파이썬은 참조 자체를 값으로 전달, 전통적 call by reference와 다름
마무리
파이썬의 인수 전달은 call by object reference 또는 call by sharing으로 이해하는 것이 정확 핵심은 이름 바인딩과 객체 가변성의 조합
- 재바인딩은 지역에만 영향
- 가변 객체의 in-place 변경은 호출자에게도 관찰됨
이 규칙을 기준으로 함수 인터페이스 설계와 데이터 구조 선택을 일관되게 가져가면 예측 가능한 코드 유지 가능