파이썬에서는 배열을 선언할 때 여러가지 방법들이 있다.

 

list_1 = []
list_2 = [] * 5
list_3 = [] for _ in range(5)

등등 여러가지가 있는데, 그중에서도 오늘은 for 문과 * 연산자를 이용한 배열 선언의 차이에 대해서 적어보고자 한다.

 

2차원 배열을 선언할 때는 3가지 방법을 쓸 수 있다.

 

 

첫 번째로 빈 리스트 생성 후 append로 더하기이다.

# 아예 빈 리스트를 생성 후 append로 더하기

list_1 = []
list_1.append([0, 0, 0])

print(list_1)

# [[0, 0, 0]]

다만 이 방법은 추후 코드를 통해서 append 해야하기 때문에, 실질적으로 append 하기 전까진 1차원 배열이라고 할 수 있다.

따라서 다음과 같은 코드를 실행하는 데 문제가 발생한다.

# Index Error 발생

list_1 = []

# IndexError: list index out of range
print(list_1[0][1])

list_1.append([0, 0, 0])

따라서 위와 같은 문제를 해결하기 위해 2가지 방법을 더 생각해 볼 수 있다.

 

 

두 번째로 * 연산자를 이용한 선언이다.

# * 연산자를 이용한 선언

list_1 = [[0] * 3] * 5

print(list_1[0][1])

list_1.append([1, 1, 1])

# 0

 

 

세 번째로는 for문을 이용한 선언이다.

# for문을 이용한 선언

list_1 = [[0 for col in range(3)] for row in range(5)]

print(list_1[0][1])

list_1.append([1, 1, 1])

# 0

 

두 번째와 세 번째를 모두 실행해보면 같은 결과를 출력하는 걸 볼 수 있다.

그렇기에 둘 다 같은 거라는 생각이 들 수 있다.

하지만 둘은 엄연히 다르다.

 

 

이걸 설명하기 위해서는 깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)에 대해서 알아야한다.

우선 깊은 복사는 변수나 객체가 가지고 있는 값을 그대로 복사하여, 다른 변수나 객체가 동일한 값을 가지게 하는 것이고,

얕은 복사는 변수나 객체를 참조하는 것이다.

 

즉 깊은 복사는 원본 객체와 복사된 객체는 별개의 객체이지만, 얕은 복사는 복사된 객체가 원본 객체를 참조하기 때문에 둘간의 연결성이 있다.

 

# 세로 길이가 3, 가로 길이가 2인 행렬이 있다고 했을 때,
# 이를 2차원 배열로 바꾸는 2가지 방법의 차이점

# 얕은 복사
list_1 = [[False] * 2] * 3
# 깊은 복사
list_2 = [[False for col in range(2)] for row in range(3)]

print(list_1)
print(list_2)
# [[False, False], [False, False], [False, False]]
# [[False, False], [False, False], [False, False]]

list_1[0][0] = True
list_2[1][1] = True

print(list_1)
print(list_2)
# [[True, False], [True, False], [True, False]]
# [[False, False], [False, True], [False, False]]

위와 같이 list_1은 [False] * 2로 각각의 요소들을 얕은 복사를 했기에,

[0][0]뿐만 아니라 [1][0], [2][0]까지도 값이 변경된 것을 확인 할 수 있다.

 

반대로 list_2는 for문을 통한 깊은 복사를 했기에,

[1][1] 요소만 변경되고, 나머지 요소에는 영향을 주지 않은 것을 볼 수 있다.

 

 

간혹 함수에 매개변수 값을 전달할 때 자주 나오는 pass by reference, pass by value도 이와 비슷한 맥락이라 볼 수 있다.

둘이 완전히 동일하진 않지만, 값을 전달 할 것인가, 참조할 것인가의 차이인 점에서는 같다.

 

 

 

파이썬에 익숙하지 않다보니, 얕은 복사인지도 모르고 쓰다가 수십 분을 허비한 경험으로 글을 쓰게 되었다.

다들 쉽다고 하는데, 왜 나는 이렇게 파이썬이 어려운지 잘 모르겠다

차근차근 시간을 두고 익혀봐야겠다.

'Language > Python' 카테고리의 다른 글

[Python] List Slicing  (0) 2022.08.09
# Created by ARA on 2022-08-09.

# 이것이 코딩테스트다
# Ch05-DFS/BFS-꼭 필요한 자료구조 기초

stack = []

# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop()
stack.append(4)
stack.pop()

print(stack)
print(stack[::-1])

# Expected Output
# [5, 2, 3]
# [3, 2, 5]

코딩테스트 책을 보는데, 18번 줄에서의 stack[::-1]이 무엇을 의미하는지 이해가 안됐다.

그래서 인터넷을 찾아보고 알아본 걸 정리해본다.

 

일단 우선 python [] 연산자에 대해서 찾아봤다.

c++ 에서 검색했던 것과는 다르게 별 다른 내용이 나오질 않았다.

 

어쩌다보니 slicing이라는 키워드를 찾게 되었는데, 거기에 해당하는 내용이 있었다.

(막상 정식 문서 가서 찾아보려니 위와 같은 예제는 찾질 못하겠다... 혹시라도 나중에 찾게 되면 추가하겠다.)

 

이 사이트에서 문법을 설명하는데, 내가 알고 있는 것과 동일했다.

다만 뒤에 Step이 있는 지 몰랐다.

자세히 읽어보니 다음 요소(element)를 선택할 때 기준을 말하는 거 같았다.

 

# Return every 2nd item between position 2 to 7
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[2:7:2])
# Prints ['c', 'e', 'g']

예제 코드를 해석해 봤을 때, L 리스트에 있는 요소 중에서 2번 인덱스 부터 7번 미만 인덱스 까지 2의 간격으로 요소를 출력하라고 해석해볼 수 있다.

그림으로 표현하면 다음과 같다

'Language > Python' 카테고리의 다른 글

[Python] 2차원 배열을 선언할 때 주의할 점  (0) 2022.08.10

+ Recent posts