개발자/파이썬 Python

파이선 자료구조 List 사용법. 리얼 파이선 13

지구빵집 2022. 2. 22. 09:25
반응형

 

 

파이썬에는 다른 언어와 비슷하게 자료를 배열처럼 사용하는 방법이 있습니다. 물론 다른 언어에서 가지고 있는 배열과 비슷한 특징을 가지고 있으며, 추가 기능이 많습니다. 파이썬의 자료구조 형태중 하나로 List 리스트는 값을 순서대로 나열하는 데이터 구조라고 정의할 수 있겠습니다. 또한 List는 시퀀스 데이터 이기도 합니다. 시퀀스는 데이터의 순서를 정하는 것을 의미합니다. 순서를 가지고 있기 때문에 인덱스(index)를 사용하여 참조할 수 있습니다. 인덱스란 순서가 있는 항목의 일련번호를 뜻합니다.

 

파이선 자료구조 List 사용법. 리얼 파이선 13 

 

파이썬에서의 시퀀스 자료형은(순서를 정함) string, list, tuple 자료형이 있으며, 시퀀스 자료형이 아닌 것은(순서를 정하지 않음) set, dictionary 가 있습니다.

 

파이썬은 다른 값들을 덩어리로 묶는 데 사용되는 여러 가지 컴파운드 (compound) 자료 형을 가지고 있습니다. 가장 융통성이 있는 것은 리스트인데, 대괄호 사이에 쉼표로 구분된 값(항목)들의 목록으로 표현될 수 있습니다. 리스트는 서로 다른 데이터형 항목들을 포함할 수 있지만, 항목들이 모두 같은 형인 경우가 많습니다.

 

리스트에 대해 9가지 기능을 간단하게 알아보고, 리스트가 가지고 있는 매쏘드(함수)와 기능을 자세히 알아보겠습니다.

 

>>> squares = [1, 4, 9, 16, 25]
>>> squares
[1, 4, 9, 16, 25]

 

문자열(그리고, 다른 모든 내장 시퀀스 형들)처럼 리스트는 인덱싱하고 슬라이싱할 수 있습니다: 

>>> squares[0]  # indexing returns the item
1
>>> squares[-1]
25
>>> squares[-3:]  # slicing returns a new list
[9, 16, 25]

모든 슬라이스 연산은 요청한 항목들을 포함하는 새 리스트를 돌려줍니다. 이는 다음과 같은 슬라이스가 리스트의 새로운 얕은 복사본을 돌려준다는 뜻입니다:

>>> squares[:]
[1, 4, 9, 16, 25]

 

리스트는 이어붙이기 같은 연산도 지원합니다:

>>> squares + [36, 49, 64, 81, 100]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

 

불변 인 문자열과는 달리, 리스트는 가변입니다. 즉 내용을 변경할 수 있습니다:

>>> cubes = [1, 8, 27, 65, 125]  # something's wrong here
>>> 4 ** 3  # the cube of 4 is 64, not 65!
64
>>> cubes[3] = 64  # replace the wrong value
>>> cubes
[1, 8, 27, 64, 125]

 

append() 메서드 (method) (나중에 메서드에 대해 더 자세히 알아볼 것입니다)를 사용하면 리스트의 끝에 새 항목을 추가할 수 있습니다:

>>> cubes.append(216)  # add the cube of 6
>>> cubes.append(7 ** 3)  # and the cube of 7
>>> cubes
[1, 8, 27, 64, 125, 216, 343]

 

슬라이스에 대입하는 것도 가능한데, 리스트의 길이를 변경할 수 있고, 모든 항목을 삭제할 수조차 있습니다:

>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> letters
['a', 'b', 'c', 'd', 'e', 'f', 'g']

>>> # 어떤 값들을 교체합니다.

>>> letters[2:5] = ['C', 'D', 'E']
>>> letters
['a', 'b', 'C', 'D', 'E', 'f', 'g']

>>> # 값을 제거합니다.

>>> letters[2:5] = []
>>> letters
['a', 'b', 'f', 'g']

>>> # 모든 값을 제거하고 빈 리스트를 만듭니다.

>>> letters[:] = []
>>> letters
[]

 

내장 함수 len() 은 리스트에도 적용됩니다:

>>> letters = ['a', 'b', 'c', 'd']
>>> len(letters)
4
>>>

 

리스트를 중첩할 수도 있습니다. (다른 리스트를 포함하는 리스트를 만듭니다). 예를 들어:

>>> a = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> x = [a, n]
>>> x
[['a', 'b', 'c'], [1, 2, 3]]
>>> x[0]
['a', 'b', 'c']
>>> x[0][1]
'b'

 

여기까지가 파이선 자료구조의 하나인 List에 대해 기본으로 알아야 할 것들입니다. 다음으로 위에 배운 내용을 복습하고 몇 가지 새로운 사항을 설명합니다.

 

리스트 자료 형은 몇 가지 메서드들을 더 갖고 있습니다. 이것들이 리스트 객체의 모든 메서드 들입니다:

 

● list.append(x) 리스트의 끝에 항목을 더합니다. a[len(a):] = [x]와 동등합니다.

 

● list.extend(iterable) 리스트의 끝에 이터러블의 모든 항목을 덧붙여서 확장합니다. a[len(a):] = iterable와 동등합니다.

 

● list.insert(i, x) 주어진 위치에 항목을 삽입합니다. 첫 번째 인자는 삽입되는 요소가 갖게 될 인덱스입니다. 그래서 a.insert(0, x) 는 리스트의 처음에 삽입하고, a.insert(len(a), x) 는 a.append(x) 와 동등합니다.

 

● list.remove(x) 리스트에서 값이 x 와 같은 첫 번째 항목을 삭제합니다. 그런 항목이 없으면 ValueError를 일으킵니다.

 

● list.pop([i]) 리스트에서 주어진 위치에 있는 항목을 삭제하고, 그 항목을 돌려줍니다. 인덱스를 지정하지 않으면, a.pop() 은 리스트의 마지막 항목을 삭제하고 돌려줍니다. (메서드 시그니처에서 i 를 둘러싼 대괄호는 매개변수가 선택적임을 나타냅니다. 그 위치에 대괄호를 입력해야 한다는 뜻이 아닙니다. 이 표기법은 파이썬 라이브러리 레퍼런스에서 지주 등장합니다.)

 

● list.clear() 리스트의 모든 항목을 삭제합니다. del a[:] 와 동등합니다.

 

● list.index(x[, start[, end]]) 리스트에 있는 항목 중 값이 x 와 같은 첫 번째 것의 0부터 시작하는 인덱스를 돌려줍니다. 그런 항목이 없으면 ValueError 를 일으킵니다.

 

선택적인 인자 start 와 end 는 슬라이스 표기법처럼 해석되고, 검색을 리스트의 특별한 서브 시퀀스로 제한하는 데 사용됩니다. 돌려주는 인덱스는 start 인자가 아니라 전체 시퀀스의 시작을 기준으로 합니다.

 

● list.count(x) 리스트에서 x 가 등장하는 횟수를 돌려줍니다.

 

● list.sort(*, key=None, reverse=False) 리스트의 항목들을 제자리에서 정렬합니다 (인자들은 정렬 커스터마이제이션에 사용될 수 있습니다. 설명은 sorted() 를 보세요).

 

● list.reverse() 리스트의 요소들을 제자리에서 뒤집습니다.

 

● list.copy() 리스트의 얕은 사본을 돌려줍니다. a[:] 와 동등합니다. 

 

아래는 리스트가 제공하는 메서드 대부분을 사용하는 예를 보여주는 코드입니다. 여러 번 연습하시기 바랍니다.: 

 

>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)  # Find next banana starting a position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'

 

▶ 리스트를 스택으로 사용하기

 

리스트 메서드들은 리스트를 스택으로 사용하기 쉽게 만드는데, 마지막에 넣은 요소가 처음으로 꺼내지는 요소입니다 (《last-in, first-out》). 스택의 꼭대기에 항목을 넣으려면 append() 를 사용하세요. 스택의 꼭대기에서 값을 꺼내려면 명시적인 인덱스 없이 pop() 을 사용하세요. 예를 들어: 

 

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

 

▶ 리스트를 큐로 사용하기 

 

리스트를 큐로 사용하는 것도 가능한데, 처음으로 넣은 요소가 처음으로 꺼내지는 요소입니다 (《first-in, first-out》); 하지만, 리스트는 이 목적에는 효율적이지 않습니다. 리스트의 끝에 덧붙이거나, 끝에서 꺼내는 것은 빠르지만, 리스트의 머리에 덧붙이거나 머리에서 꺼내는 것은 느립니다(다른 요소들을 모두 한 칸씩 이동시켜야 하기 때문입니다).

 

큐를 구현하려면, 양 끝에서의 덧붙이기와 꺼내기가 모두 빠르도록 설계된 collections.deque 를 사용하세요. 예를 들어: 

 

>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           # Terry arrives
>>> queue.append("Graham")          # Graham arrives
>>> queue.popleft()                 # The first to arrive now leaves
'Eric'
>>> queue.popleft()                 # The second to arrive now leaves
'John'
>>> queue                           # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])

 

▶리스트 컴프리헨션 - 컴프리헨션은 파이썬의 자료구조(list, dictionary, set)에 데이터를 좀 더 쉽고 간결하게 담기 위한 문법입니다. 여기서 말하는 '쉽고 간결하게' 데이터를 담는 방법이란 반복문과 조건문을 결합하여 하나의 구문으로 만들어 담는 것을 의미합니다. 처음 만나면 복잡하고 이상한 생김새에 당황하실 수 있지만, 익숙해지만 정말 편하고 좋은 문법입니다. 전문가들은 이런 것들을 많이 알고 있는 사람입니다. 

 

리스트 컴프리헨션은 리스트를 만드는 간결한 방법을 제공합니다. 흔한 용도는 각 요소가 다른 시퀀스나 이터러블의 멤버들에 어떤 연산을 적용한 결과인 리스트를 만들거나, 어떤 조건을 만족하는 요소들로 구성된 서브 시퀀스를 만드는 것입니다.

 

예를 들어, 제곱수의 리스트를 만들고 싶다고 가정하자, 이런 식입니다: 

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

 

이것은 x 라는 이름의 변수를 만들고 (또는 덮어쓰고) 루프가 종료된 후에도 남아있게 만든다는 것에 유의하세요. 어떤 부작용도 없이, 제곱수의 리스트를 이런 식으로 계산할 수 있습니다:

squares = list(map(lambda x: x**2, range(10)))

 

또는, 이렇게 할 수도 있습니다:

squares = [x**2 for x in range(10)]

 

이것이 더 간결하고 읽기 쉽습니다.

 

리스트 컴프리헨션은 표현식과 그 뒤를 따르는 for 절과 여러 개의 for 나 if 절들을 감싸는 대괄호로 구성됩니다. 그 결과는 새 리스트인데, for 와 if 절의 문맥에서 표현식의 값을 구해서 만들어집니다. 예를 들어, 이 리스트 컴프리헨션은 두 리스트의 요소들을 서로 같지 않은 것끼리 결합합니다:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

 

그리고, 이것은 다음과 동등합니다:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

 

두 코드 조각에서 for 와 if 문의 순서가 같음에 유의하세요.

 

표현식이 튜플이면 (즉 앞의 예에서 (x, y)), 반드시 괄호로 둘러싸야 합니다.

>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1, in <module>
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

리스트 컴프리헨션은 복잡한 표현식과 중첩된 함수들을 포함할 수 있습니다:

>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

 

▶중첩된 리스트 컴프리헨션 

 

리스트 컴프리헨션의 첫 표현식으로 임의의 표현식이 올 수 있는데, 다른 리스트 컴프리헨션도 가능합니다.

다음과 같은 길이가 4인 리스트 3개의 리스트로 구현된 3x4 행렬의 예를 봅시다:

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

 

다음 리스트 컴프리헨션은 행과 열을 전치시킵니다:

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

 

앞절에서 보았듯이, 중첩된 리스트 컴프리헨션은 뒤따르는 for 의 문맥에서 값이 구해집니다. 그래서 이 예는 다음과 동등합니다:

>>> transposed = []
>>> for i in range(4):
...     transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

 

이것은 다시 다음과 같습니다:

>>> transposed = []
>>> for i in range(4):
...     # the following 3 lines implement the nested listcomp
...     transposed_row = []
...     for row in matrix:
...         transposed_row.append(row[i])
...     transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

 

실제 세상에서는, 복잡한 흐름문보다 내장 함수들을 선호해야 합니다. 이 경우에는 zip() 함수가 제 역할을 할 수 있습니다:

>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

 

이 줄에 나오는 애스터리스크에 대한 자세한 내용은 인자 목록 언 패킹을 보세요. 

 

여기까지 파이선 자료구조의 하나인 리스트에 대해 알아보았습니다. 기억해야 할 것은 리스트는 가변 시퀀스(시퀀스 자료형이란 문자열처럼 값이 연속적으로 이어진 자료 형태를 말함)로 대부분 같은 데이터 형태 항목을 모아 저장하는 데 사용한다는 것입니다.

 

 

 

 

 

 

 

반응형