[Python] 깊은 복사와 리스트 컴프리헨션
두 객체의 비교와 복사
객체를 비교할 때 사용하는 연산자는 두가지이다.
v1 == v2 // 변수 v1과 v2가 참조하는 객체의 내용이 같은가?
v1 is v2 // 변수 v1과 v2가 참조하는 객체는 동일 객체인가?
>>> r1 = [1,2,3]
>>> r2 = [1,2,3]
>>> r1 is r2
False
>>> r1 == r2
True
>>> r1 = ['John', ('man','USA'),[175,23]]
>>> r2 = list(r1)
>>> r1 is r2
False
>>> r1[0] is r2[0]
True
>>> r1[1] is r2[1]
True
>>> r1[2] is r2[2]
True
r1 = [‘John’, (‘man’,’USA’),[175,23]] 을 만들면 리스트 안에 값들이 쏙 들어가는 형태가 아니라, ‘John’ 문자열 객체를 참조하고 (‘man’,’USA’) 튜플 객체를 참조하고 [175,23] 리스트 객체를 참조하는 형태가 된다.
r2 = list(r1)을 하게되면 r2역시 r1과 같이 리스트 안에 있는 문자열, 튜플, 리스트 객체를 참조하는데 이때 참조하는 객체가 r1, r2가 동일하다.
이러한 형태의 복사를 ‘얕은 복사’라고 한다. 이 방식은 파이썬이 복사를 진행하는 기본방식이다.
여기서 문자열 객체나 튜플객체는 immutable객체이므로 얕은 복사를 해도 문제가 되지 않지만 리스트 객체의 경우 그 안에 담긴 값을 수정할 수 있어 문제가 될 수 있다.
깊은 복사
값 변경이 불가능한 immutable 객체는 얕은 복사를 해도 문제가 되지 않지만 값 변경이 가능한 mutable 객체는 얕은 복사가 문제가 될 수 있다.
위의 코드에서 r1의 리스트를 수정하면 r2의 리스트 내용도 수정되는 문제가 있다.
그래서 immutable 객체는 얕은 복사, mutable 객체는 깊은 복사를 진행해 안전성과 성능을 모두 만족시킬 수 있다.
이때 사용하는 것이 copy 모듈의 deepcopy 함수를 사용할 수 있다.
>>> J2021 = ['John', ('man','USA'),[175,23]]
>>> import copy
>>> J2022 = copy.deepcopy(J2021)
>>> J2022[2][1] += 1
>>> J2021
['John', ('man','USA'),[175,23]]
>>> J2022
['John', ('man','USA'),[175,24]]
>>> (J2021[0] is J2022[0]) and (J2021[1] is J2022[1]) #얕은 복사 확인
True
>>> J2021[2] is J2022[2] #깊은 복사 확인
False
리스트 생성 방법
>>> r1 = [1,2,3]
>>> r2 = []
>>> r3 = [1,2,[3,4]]
>>> r4 = list('Hello')
>>> r5 = list((5,6,7))
>>> r6 = list(range(0,5))
리스트 컴프리헨션
1. 기본구조
>>> r1 = [1,2,3,4,5]
>>> r2 = []
>>> for i in r1:
r2.append(i*2)
>>> r2
[2,4,6,8,10]
>>> r1 = [1,2,3,4,5]
>>> r2 = [x*2 for x in r1] # 리스트 컴프리헨션의 기본 구조
>>> r2
[2,4,6,8,10]
리스트를 만들 때 for 루프를 사용하지 않는 형태로 만들 수 있고, 위 코드의 형태로 대신할 수 있다. 이것이 바로 리스트 컴프리헨션(Comprehension)이다.
2. 조건 필터 추가
>>> r1 = [1,2,3,4,5]
>>> r2 = []
>>> for i in r1:
if i % 2:
r2.append(i*2)
>>> r2
[2,6,10]
>>> r1 = [1,2,3,4,5]
>>> r2 = [x*2 for x in r1 if x%2] # if절이 추가된 리스트 컴프리헨션
>>> r2
[2,6,10]
리스트 컴프리헨션은 일종의 규칙이므로 이해보다는 적용에 무개를 둬서 학습하는 것이 바람직하다.
3. for 한번 더 들어가는 경우
>>> r1 = ['Black', 'White']
>>> r2 = ['Red', 'Blue', 'Green']
>>> r3 = []
>>> for t in r1:
for p in r2:
r3.append(t+p)
>>> r3
['BlackRed', 'BlackBlue', 'BlackGreen', 'WhiteRed', 'WhiteBlue', 'WhiteGreen']
>>> r1 = ['Black', 'White']
>>> r2 = ['Red', 'Blue', 'Green']
>>> r3 = [t+p for t in r1 for p in r2] # 중첩된 for 루프 형태의 리스트 컴프리헨션
>>> r3
['BlackRed', 'BlackBlue', 'BlackGreen', 'WhiteRed', 'WhiteBlue', 'WhiteGreen']
>>> r = [n*m for n in range(2,10) for m in range(1,10)]
>>> r
[2,4,6,8,10,12,14,16,18,3,6,9,12,15,18,21,24,27,4 ... 중간 생략 ... 45,54,63,72,81]
4. 이중 for 루프에 조건 필터 추가
>>> r = [n*m for n in range(2,10) for m in range(1,10) if(n*m)%2]
>>> r
[3,9,15,21,27,5,15,25,35,45,7,21,35,49,63,9,27,45,63,81]
구구단을 만들면서 결과가 홀수인 값들만 리스트에 포함시켰다.