[Python] 깊은 복사와 리스트 컴프리헨션

2 분 소요

두 객체의 비교와 복사

객체를 비교할 때 사용하는 연산자는 두가지이다.

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]

구구단을 만들면서 결과가 홀수인 값들만 리스트에 포함시켰다.