Python의 함수 호출 방식: 객체 참조에 의한 호출
Python에서 함수 호출 방식은 다른 프로그래밍 언어와 비교할 때 독특하다. Python은 객체 참조에 의한 호출(Call by Object Reference) 방식을 사용하며, 이로 인해 함수 호출 시 값이 전달되는 방식이 다르게 작동한다. Python의 호출 방식이 무엇인지, 다른 언어와 어떤 차이점이 있는지, 그리고 이 방식이 메모리 관리에 어떤 영향을 미치는지 살펴보자.
Python의 객체 참조에 의한 호출
Python에서 매개변수가 저장하는 것은 실제 값이 아니라 객체에 대한 참조이다. 함수를 호출할 때, 인자로 전달된 변수는 객체의 참조를 전달한다. 이때 중요한 점은, 함수가 참조를 받지만 그 객체 자체를 수정할 수 있느냐는 객체의 변경 가능성(Mutability)에 따라 다르다는 것이다.
변경 불가능한 객체 (Immutable Object)
변경 불가능한 객체에는 정수(int), 문자열(str), 튜플(tuple) 등이 있다. 이런 객체를 함수에 전달하면, 함수 내에서 해당 객체의 값을 변경하는 것이 불가능하다. 만약 함수 내부에서 변수를 수정하면, 실제로는 새로운 객체가 생성되고, 함수 내의 변수는 이 새로운 객체를 참조하게 된다. 따라서 원본 객체는 변경되지 않는다.
def modify(x):
x = 10 # 새로운 객체를 참조
a = 5
modify(a)
print(a) # 출력: 5
위 코드에서 a
는 함수 호출 이후에도 여전히 원래의 값(5)을 유지한다. 이는 함수 modify
가 a
의 값을 수정하는 대신 새로운 정수 객체 10
을 가리키도록 x
를 변경했기 때문이다.
변경 가능한 객체 (Mutable Object)
변경 가능한 객체에는 리스트(list), 딕셔너리(dict), 집합(set) 등이 있다. 이런 객체를 함수에 전달하면, 함수는 그 객체의 내용을 직접 수정할 수 있다. 이는 함수가 받은 객체 참조가 원본 객체를 가리키기 때문이다.
def modify(lst):
lst.append(10) # 원본 리스트가 수정됨
a = [1, 2, 3]
modify(a)
print(a) # 출력: [1, 2, 3, 10]
위 코드에서는 리스트 a
가 함수 내에서 수정되어 함수 호출 이후에도 a
에 변경된 값이 반영된다. 이는 리스트가 변경 가능한 객체이기 때문에 함수가 직접 원본 객체를 수정할 수 있기 때문이다.
Python과 다른 언어의 호출 방식 비교
C 같은 언어에서는 함수 호출 방식이 값에 의한 호출(Call by Value)과 포인터에 의한 호출(Call by Pointer)로 구분된다. 값에 의한 호출에서는 인자의 복사본이 함수로 전달되기 때문에, 함수 내부의 변경이 원본에 영향을 미치지 않는다. 반면, 포인터에 의한 호출에서는 인자의 메모리 주소가 전달되어 함수가 직접 원본 데이터를 수정할 수 있다.
Python은 이 둘의 특성을 모두 가진 객체 참조에 의한 호출 방식을 사용한다. 변경 불가능한 객체는 C의 값에 의한 호출과 유사하게 동작하고, 변경 가능한 객체는 C의 포인터에 의한 호출처럼 동작한다. 하지만 Python에서는 이 모든 것이 객체 참조를 통해 이루어진다는 점에서 차이가 있다.
결론
Python의 함수 호출 방식인 객체 참조에 의한 호출은 Python의 중요한 특징 중 하나다. 이 방식은 변수와 객체 간의 관계를 통해 함수 내에서 객체를 어떻게 다루는지를 결정한다. 변경 불가능한 객체는 함수 내에서 직접 수정할 수 없으며, 새로운 객체를 참조하게 된다. 반면, 변경 가능한 객체는 함수 내에서 수정되며, 이 변경 사항은 호출자에게도 그대로 반영된다. 이러한 동작 방식은 Python의 메모리 관리 방식과 밀접하게 연관되어 있으며, 이를 이해하는 것은 Python 코드를 효율적으로 작성하는 데 매우 중요하다.