"값에 의한 전달"(pass by value) / "참조에 의한 전달"(pass by reference) / 객체 참조에 의한 전달"(pass by object reference) /"값에 의한 호출"

FSA·2024년 2월 12일
0

python 기초

목록 보기
32/65

1. 문제코드

class ABC:
    def __init__(self, a: int, b: int):
        self.a = a
        self.b = b

def change_value(abc: ABC):
    abc.a = 10
    abc.b = 20

def change_value2(a: int, b: int):
    a = 10
    b = 20

abc = ABC(1, 2)
change_value2(abc.a, abc.b)
print(abc.a, abc.b)  # 1 2
change_value(abc)
print(abc.a, abc.b)  # 10 20

2. 개념 설명

"값에 의한 전달"(pass by value)과 "참조에 의한 전달"(pass by reference)은 함수에 인자를 넘기는 방식을 설명할 때 사용되는 용어입니다. 이 두 방식은 함수가 인자의 데이터를 어떻게 처리하는지에 대한 핵심적인 차이점을 나타냅니다.

2.1. 값에 의한 전달 (Pass by Value) / "값에 의한 호출"

  • 이 방식에서는 함수에 인자를 전달할 때 인자의 실제 값(value)의 복사본이 생성되어 함수에 전달됩니다.
  • 함수 내에서 인자의 값을 변경해도, 그 변경이 원래 변수에 영향을 미치지 않습니다. 즉, 함수 외부의 변수는 함수 내부에서의 변경으로부터 안전합니다.
  • 기본 데이터 타입(정수, 실수, 문자열 등)을 다룰 때 많은 프로그래밍 언어에서 이 방식이 사용

2.1.1. 장점

  • 안정성: 함수 내부에서 데이터를 변경해도 외부의 원본 데이터에 영향을 미치지 않으므로, 데이터가 안전하게 보호됩니다.
  • 예측 가능성: 함수가 인자로 받은 값에 대해 어떤 연산을 수행해도, 그 연산이 외부 변수에 부수 효과(side effect)를 일으키지 않으므로, 프로그램의 흐름을 예측하기 쉽습니다.

2.1.2. 단점

  • 메모리 사용 증가: 큰 데이터를 함수에 전달할 때마다 그 데이터의 사본을 만들어야 하므로, 메모리 사용량이 증가할 수 있습니다.
  • 성능 저하: 특히 큰 사이즈의 데이터를 다룰 때, 데이터의 사본을 만드는 데 시간이 걸릴 수 있어, 프로그램의 실행 속도가 느려질 수 있습니다.

2.2. 참조에 의한 전달 (Pass by Reference)

  • (나머지 데이터 타입)
  • 이 방식에서는 함수에 인자를 전달할 때, 인자의 메모리 주소(reference)가 함수에 전달됩니다.
  • 함수 내부에서 인자의 값을 변경하면, 그 변경이 원래 변수에도 반영됩니다. 이는 함수가 변수의 실제 메모리 주소를 가지고 있기 때문에 가능합니다.
  • 복잡한 데이터 구조(객체, 리스트, 딕셔너리 등)를 다룰 때 많은 프로그래밍 언어에서 이 방식이 사용됩니다.

2.3. Python에서의 동작

Python은 좀 더 세밀한 차이를 가지고 있는데, "객체 참조에 의한 전달"(pass by object reference) 방식을 사용합니다. 이는 함수에 인자를 전달할 때, 객체의 참조(메모리 주소)가 전달되지만, 객체 자체가 아니라 객체의 참조(메모리 주소)가 복사되어 전달된다는 점에서 참조에 의한 전달과 다릅니다. 따라서,

  • 변경 불가능한 객체(immutable)인 경우(예: 문자열, 튜플, 정수 등)
    • 함수 내에서 이러한 객체를 변경하려 하면 새로운 객체가 생성되고, 원래 객체는 변경되지 않음
  • 변경 가능한 객체(mutable)인 경우(예: 리스트, 딕셔너리, 클래스 인스턴스 등)
    • 함수 내에서 객체를 변경하면 원본 객체에도 이 변경사항이 반영됩니다.
def add_item(list_example):
    list_example.append(4)  # 기존 객체를 변경

def change_reference(list_example):
    list_example = [4, 5, 6]  # 새로운 객체로 참조를 변경

my_list = [1, 2, 3]
add_item(my_list)
print(my_list)  # 출력: [1, 2, 3, 4], 기존 객체가 변경됨

change_reference(my_list)
print(my_list)  # 출력: [1, 2, 3, 4], 외부의 my_list는 변경되지 않음

3. 해결

change_value2 함수를 호출했을 때 abc.aabc.b가 바뀌지 않는 이유는 Python에서 함수에 인자를 전달할 때 "값에 의한 전달"(pass by value) 방식이 아니라 "참조에 의한 전달"(pass by reference) 방식을 사용하지만, 이는 기본 타입(예: 정수, 실수, 문자열 등)에 대해서는 실질적으로 "값에 의한 호출"처럼 동작하기 때문입니다. 즉, change_value2 함수에 abc.aabc.b를 인자로 전달하면, 이들의 값이 복사되어 전달되며, 함수 내에서 ab 변수의 값을 변경해도 원본 abc.aabc.b에는 영향을 주지 않습니다.

3.1. 해결 방안

abc.aabc.b를 변경하고 싶다면, 몇 가지 해결 방안이 있습니다:

  1. 객체를 직접 전달하기: change_value 함수처럼 객체 자체를 전달하고, 함수 내에서 객체의 속성을 변경합니다. 이 방법은 이미 코드에서 구현되어 있으며, 객체의 속성을 직접 변경하여 원하는 결과를 얻습니다.

  2. 값을 반환하여 업데이트하기: change_value2 함수를 수정하여 ab의 새로운 값을 반환하고, 이를 사용하여 abc.aabc.b를 업데이트할 수 있습니다. 예를 들어:

class ABC:
    def __init__(self, a: int, b: int):
        self.a = a
        self.b = b

def change_value(abc: ABC):
    abc.a = 10
    abc.b = 20

def change_value2(a: int, b: int) -> (int, int):
    a = 10
    b = 20
    return a, b

abc = ABC(1, 2)
abc.a, abc.b = change_value2(abc.a, abc.b)
print(abc.a, abc.b)  # 출력: 10 20
change_value(abc)
print(abc.a, abc.b)  # 출력: 10 20

이 방식에서 change_value2 함수는 새로운 a, b 값을 반환하고, 이를 통해 abc.a, abc.b를 업데이트합니다. 이 방법은 함수가 값을 변경하고 그 결과를 호출자에게 명시적으로 반환할 때 유용합니다.

3.2. 결론

Python에서 기본 타입의 값은 함수에 인자로 전달될 때 값이 복사되어 전달되기 때문에, 함수 내부에서 이 값을 변경해도 원본에 영향을 주지 않습니다. 객체의 속성을 변경하고자 할 때는 객체를 직접 전달하거나, 변경된 값을 반환하고 호출자가 이를 사용하여 객체를 업데이트하는 방법을 사용해야 합니다.

profile
모든 의사 결정 과정을 지나칠 정도로 모두 기록하고, 나중에 스스로 피드백 하는 것

0개의 댓글