리스트는 값을 변경, 삭제, 추가 할 수 있고 메모리를 많이 차지한다.
따라서 조금 특이한 구조가 발생하는데, 예시로 알아보자
prac_result4 = [str(x) for x in list(range(2,11))] + [x for x in 'JQKA']
print(prac_result4)
해당 코드에서 prac_result4라는 변수를 만들었다.
['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
이 리스트는 prac_result4라는 이름으로 메모리에 저장되어있다.
prac_result5 = [prac_result4 for x in range(4)]
print(prac_result5)
이 코드로 prac_result5라는 변수에 prac_result4를 4회 반복한 값을 집어넣는다.
[['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'], ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'], ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'], ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']]
이 리스트는 prac_result5라는 이름으로 메모리에 저장되었다.
이러고 나서
prac_result4[0] = 'two'
prac_result4의 0번째 인덱스를 'two'로 변경해보면
[['two', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],
['two', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],
['two', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],
['two', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']]
prac_result5가 변수가 바뀌어있다.
왜 그럴까
- prac_result5는 메모리의 주소로 변수(prac_result4)를 4번 복사한 값을 가지고 있다.
- 따라서 pract_result4를 바꾸면
- prac_result5가 prac_result4를 보고 있었기 때문에, prac_result5의 결과값이 바뀌는 걸 볼 수 있다.
효율적 메모리 사용
변화가 큰 자료형들은 주소를 가지고 있어서, 주소를 메모리 상에 가지고 있다.
위의 모습은 객체를 담는 변수가 메모리상 객체의 위치를 복사했기 때문에
prac_result5를 호출했을 때, prac_result4의 값을 참조했기 때문에 이런 결과가 나온다고 할 수 있다.
id() 내장함수
객체마다 구분할 수 있는 고유한 정수 값(메모리 주소)을 갖습니다. 그걸 조회하는게 id() 내장함수 입니다.
즉 id()로 가져온 정수 값이 같다면 같은 객체라는 의미입니다.
prac_result4와 prac_result5를 구성하는 모든 요소들의 id() 반환 값이 같습니다.
즉 변수와 모든 요소들이 같은 메모리상의 객체를 바라본다는 의미입니다.
print('prac_result4', id(prac_result4))
for idx, i in enumerate(prac_result5):
print(f'prac_result5{}',id(i))
### 결과값
prac_result4 2176817119296
prac_result5[0]2176817119296
prac_result5[1]2176817119296
prac_result5[2]2176817119296
prac_result5[3]2176817119296
위의 결과로 같은 메모리상의 객체를 보는 것을 확인 할 수 있다.
기존 리스트의 전체 범위의 슬라이싱 결과는 새로운 객체이다.
리스트변수명[:]으로 전체를 동일하게 가져오는, 즉 슬라이싱으로 반환 받는 리스트 객체는 과연 같은 객체일까?
- 다른 객체이다. 완전히 새로운 객체이다.
old_list = list(range(10))
new_list = old_list[:]
print(id(old_list))
print(id(new_list))
print(old_list is new_list)
print(old_list == new_list)
### 결과값
### 2566027092288
### 2566027325504
### False
### True
위의 예제를 통해서 알 수 있는 것은
- is 함수는 메모리 상 ID를 비교
- ==은 값 자체를 비교
문자열이나 튜플은 슬라이싱을 해도 새로운 객체를 가져오지 않는다.
문자열, 튜플의 경우에는 불변(immutable)의 특징을 갖습니다. 따라서 요소가 동일하다면 재사용해도 문제 될 것이 없습니다. 그래서 효율을 위해 동일하게 복사하기도 합니다.
str_sample = "This is str"
str2 = str_sample[:]
print(id(str_sample) == id(str2))
### 결과값
### True
old_tuple = tuple(range(10))
new_tuple = old_tuple[:]
print(id(old_tuple))
print(id(new_tuple))
print(old_tuple is old_tuple)
print(old_tuple == old_tuple)
### 결과값
2656561232
2656561232
True
True
리스트 변수를 할당해서 복사하는 경우
리스트 객체를 담고 있는 변수는 메모리 주소 정보를 담고 있습니다. 그리고 그 변수를 다른 변수에 대입하게 되면 메모리 주소 정보를 복사합니다. 즉 그 두 변수는 동일한 객체를 바라보게 됩니다.
list_e = [i for i in range(10)]
list_f = list_e
print(list_f)
print(id(list_e))
print(id(list_f))
### 결과값
###
### 2176816856216
### 2176816856216
따라서 list_e[0] 값을 바꾸면 list_f[0]의 값도 변경되는 것을 알 수 있다.
리스트 복사(copy() 메서드)
리스트.copy() 메서드는 리스트 객체를 새롭게 복사합니다. 이 반환 값은 새로운 객체가 됩니다. 아래 코드의 예제를 보겠습니다. list_g로부터 copy() 메서드의 반환 값을 list_h에 할당하고 있습니다. 이 두 리스트 객체는 서로 다름을 보여주고 있습니다.
list_g = list(range(10))
list_h = lisg_g.copy()
print(id(list_g))
print(id(list_h))
print(list_g is list_h)
### 결과값
### 2176817068736
### 2176817121984 -> copy() 메서드로 만든 객체는 새롭게 만든 객체이다.
### False
list_g[0]의 요소를 변경해보겠습니다.
list_g[0] = 999
print(list_g)
print(list_h)
### 결과값 -> 서로 다른 객체이기에 서로 간에 영향이 없습니다.
얕은 복사(Shallow copy)
리스트 내에 있는 값을 그대로 복사해옵니다. 리스트 요소 중 메모리 주소를 담고 있는 참조 변수가 있으면 객체 주소만 복사해옵니다. 아래 예제를 보시다. 리스트에 애스터리스크(*)를 사용하면 얕은 복사가 됩니다. 즉 list_i[0]과 list_i[1]과 list_i[2]는 모두 같은 곳을 바라보고 있습니다.
list_i = [[0]] * 3
print(list_i)
list_i[0][0] = 999
printr(list_i)
### 결과값
### [[999],[999],[999]]
단 하나의 요소만 바꿨을 뿐인데 모든 요소의 값이 변경된 것을 알 수 있다.
요소가 객체로 되어있으면 주소만 복사하는 것을 얕은 복사라고 한다.
- 뿐만 아니라 슬라이싱, copy() 메서드를 통한 복사도 얕은 복사입니다.
깊은 복사(deep copy)
리스트 내 요소가 메모리 주소를 담고 있는 객체더라도 객체를 완전하게 깊이에 상관없이 다 새롭게 만듭니다.
전혀 다른 메모리 공간을 차지하는 완전한 리스트로 복사합니다. 두 리스트 간에 값은 같으나(==) 리스트 안의 요소까지 완전히 같은 리스트를 만듭니다.
deepcopy() 를 위해서 copy 라이브러리를 필요로 합니다.
import copy
list_n = [[9,10],[11,12]]
list_o = copy.deepcopy(list_n)
# deep copy는 요소 안에 있는 것도 전부 새롭게 만듬
print(list_n[0] is list_o
### 결과값
##@ False
자바와 파이썬의 == 비교
'데이터 분석 및 시각화 > 파이썬' 카테고리의 다른 글
[Python] 기초 정리(함수, 문자열 메서드 join) (0) | 2023.01.12 |
---|---|
[Python] 기초 정리(tuple, random, set) (0) | 2023.01.11 |
[Python] 기초 정리(dictionary, zip, list comprehension) (2) | 2023.01.10 |
[Python] 기초 정리(for문, range의 활용, while문, break, continue) (0) | 2023.01.06 |
[Python] 기초 정리(함수, 리스트 메서드, 리스트 Stack처럼 활용, unpakcing, map, if문 등) (1) | 2023.01.05 |