[CS] 카피의 모든 것

일단 제목 어그로

1. Call by Value 와 Call by Reference

a = b라는 코드가 수행 될 때에,
a에 b의 값을 따로 가지는 별도의 독립된 개체가 생성되느냐 (=값을 갖고 불리느냐=Call by Value)
a에 b의 실질 데이터가 있는 주소값을 공유하여 '참조'케 하느냐 (=실주소를 갖고 불리느냐=Call by Reference)

....의 두 가지 관점에서 시작해야한다

전자인 경우, a에 변경이 가해져도 독립된 개체이므로 b에 영향을 주지 않지만
후자의 경우, 실 데이터를 공유하는 껍데기만 다른 (그치만 같은) 객체들이기 떄문에 한 쪽의 변경은 다른 쪽에도 영향을 끼친다

 

2. Java는 어떤 방식?

이건 늘 헷갈리는 질문이 될 것이다
(본인도 이거 대학 시절 문제로 나왔다가 yes/no로만 답해서 냈던 기억이 있;;)

결론부터 이야기하면 CbV 방식이다

primitive type에서는 명확하게 CbV 방식으로 동작한다
따라서 메소드의 파라미터로 넘기던, a를 b가 이어받던 어느 쪽으로 가더라도
기존의 변수에 데이터를 건들이지 못한다

그런데 이제 reference type에서는 CbV가 조금 달리 돌아간다
그러니까 그 기존 취지에 맞게 해당 변수에는 reference를 위한 메모리 주소가 담기기 때문이다
따라서 해당 정보를 CbV로 전달하여도 새로운 독립된 아이 역시 같은 주소를 바라보게 되기 때문에
서로 끈끈한 사이가 되어버리는 것이다

 

3. 좀 더 구체적으로 - Java

일반적으로 어떤 종류 건 변수는 스택의 영역에서 생성되고, 또 객체는 힙 영역에서 생성된다.

이 때,
primitive type은 실제 데이터까지를 스택에 저장하는 한편
reference type은 실제의 데이터를 힙에 저장하고 그 주소를 스택에 저장하는 방식이다.

따라서 기본적으로 CbV로 돌아가는 자바에서는
함수의 매개변수 전달 시에 직접적인 값이 전달되는 것이 맞으나
primitive type은 실 데이터가 넘어가고, reference type은 주소가 넘어가는 격

고로
Java는 CbV가 맞으나 데이터 타입에 따라 다르게 동작한다

 

4. Python에서는?

Python은 더 환장하게 돌아간다(?)

Passed by Assignment라고도 불리우는데,
결국 어떤걸 정의하여 넘기느냐에 따라 달라진다는.....(;;;)

기본적으로 immutable type과 mutable type으로 나누어
immutable type은 CbV
mutable type은 CbR
라고 이해하자

 

5. 바꿀 수 있는 것과 없는 것에서 부터... - Python

사실 그 취지에서부터 다시 돌아가보면 이해가 쉽다

Python 내에서의 데이터들은 모두 객체라는 것을 인지하고,
그것을 메소드의 파라미터로 넘길 때에 아래 두 가지 경우로 잡아본다면,

- immutable: 실제로 바꿀 수가 없기에 -> 새 객체를 만듦 (복사)
- mutable: 바꿀 수 있는 애니까 굳이 -> 기존 객체를 참조함 (참조)

 

6. 그치만 주의해야할 사항

그렇다고 덮어놓고 "mutable은 CbR"라고만 외우지는 말자

(당연한 이야기지만)
내부 메소드에서 새 정의문을 만나게 되면 내부 지역 변수는 detach(?)가 되어버린다
-> 새로운 array가 되어버리니까

 

7. 드디어 시작되는 copy의 필요성, 그리고 개념의 분리

개념을 더 넓혀서,
알다시피 python의 list는 오만가지(!)를 한번에 담을 수 있는 그릇이 큰 아이다.

a = [1, 'asdf', [6, 'z',....]]

위와 같이 전체 데이터 타입을 통일하지 않고도 모두 짬뽕으로 담을 수 있는데
이 때에 우리가 맞닥뜨릴 수 있는 경우의 수는 3가지다

상황1) b = a
우리가 이해하고 있는대로, 주소를 공유함으로써 서로 공동체(?)인 상태
어딜 바꿔도 동일하게 바뀌...(는 것 처럼 보임). 원래는 같은 아이지.

상황2) b = copy.copy(a) - shallow copy
b에 a의 그것을 복사, 독립 개체로 맹그는 과정이나
그 대상을 immutable에 한정하는 것
따라서 immutable은 각각 독립 원소이나 mutable type은 a와 b가 공유함

상황3) b = copy.deepcopy(a) - deep copy
b에 a의 그것을 복사, 독립 개체도 맹글되,
mutable type까지 모조리 복사를 뜬 것
아주 완전하게 서로 남남인 상태를 만드는 것

 

8. 이게 왜 필요한가? copy는 copy지?

사실 이건 비단 python이 생기고서야 나타난 새 개념은 아니다
(Java에도, Javascript에도 있다)

결국 이건 경우에 따라서 두 가지 모두가 필요하기 때문이다.

캡슐화 관점에서 데이터를 은닉하면서 원하는 접근지정자가 그 데이터를 참조했을 때에
a) 딱히 원본을 건들일 필요가 없거나 b) 계속적으로 일어나는 원본의 변화를 추적해야할 때에
shallow copy가 훨씬 더 빠른 성능과 함께 정확한 요구사항을 만족할 수 있다.

즉 "shallow"나 "deep"의 관점에서라기 보다는
"share"라는 관점에서 이해한다면 이해가 쉬울 것이다.

 

#to_check
stringbuilder, stringbuffer, string