파이썬 기초 강좌: 8. 리스트 예제와 실습 (Python Basics – List Examples and Exercises)

파이썬 리스트는 매우 유용하고 강력한 데이터 구조로, 다양한 데이터를 순서대로 저장할 수 있습니다. 이 장에서는 리스트의 기본 개념부터 시작해, 리스트의 생성, 수정, 슬라이싱, 메서드 활용 및 함수 인자 등 리스트와 관련된 다양한 주제를 다룹니다. 또한, 리스트와 문자열 간의 변환, 중첩 리스트, 리스트를 이용한 파일 파싱 등의 고급 주제도 살펴봅니다. 이를 통해 파이썬 프로그래밍에서 리스트를 효과적으로 활용하는 방법을 익히게 될 것입니다.

파이썬 기초 강좌 - 리스트 예제와 실습

1. 파이썬 기초 강좌 – 리스트

리스트는 문자열처럼 값들의 시퀀스(연속적인 나열)입니다. 하지만 문자열과 리스트에는 중요한 차이점이 있습니다.

  • 문자열: 문자열은 문자(character)들의 시퀀스입니다. 즉, 문자열 안의 각 요소는 하나의 문자입니다. 예를 들어, “hello”라는 문자열은 ‘h’, ‘e’, ‘l’, ‘l’, ‘o’라는 문자들의 시퀀스입니다.
  • 리스트: 리스트는 값들의 시퀀스입니다. 문자열과 달리, 리스트는 어떤 타입의 값이든 포함할 수 있습니다. 숫자, 문자열, 또 다른 리스트 등 다양한 타입의 값을 하나의 리스트에 담을 수 있습니다.
  • (실습)
>>> cheeses = ['Cheddar', 'Edam', 'Gouda']
>>> numbers = [17, 123]
>>> empty = []
>>> print(cheeses, numbers, empty)
['Cheddar', 'Edam', 'Gouda'] [17, 123] []

>>> 리스트 = list()
>>> type(리스트)

2. 리스트는 변경 가능(mutable)

리스트의 요소에 접근하는 구문은 문자열의 문자에 접근하는 구문과 동일합니다. 하지만 문자열과 달리, 리스트는 변경(mutable) 가능하다는 중요한 차이점이 있습니다. 이는 리스트의 항목 순서를 바꾸거나, 리스트의 특정 항목을 재할당할 수 있다는 것을 의미합니다.

  • 리스트 요소 접근: 리스트의 특정 요소에 접근하는 구문은 대괄호([]) 안에 인덱스를 넣는 방식으로, 문자열의 특정 문자에 접근하는 방법과 같습니다. 인덱스는 0부터 시작합니다.
cheeses = ['Cheddar', 'Edam', 'Gouda']
print(cheeses[0])  # 출력: Cheddar
  • 리스트 변경: 리스트는 변경 가능하므로, 특정 인덱스의 값을 새로운 값으로 재할당할 수 있습니다. 이를 통해 리스트의 내용을 동적으로 변경할 수 있습니다.
numbers = [17, 123]
numbers[1] = 5
print(numbers)  # 출력: [17, 5]
  • 리스트의 요소 존재 여부 확인: in 연산자를 사용하여 리스트에 특정 요소가 포함되어 있는지 확인할 수 있습니다.
cheeses = ['Cheddar', 'Edam', 'Gouda']
print('Edam' in cheeses)  # 출력: True
print('Brie' in cheeses)  # 출력: False

3. 리스트 순회하기 (Traversing a list)

리스트의 요소를 순회(traverse)하는 가장 일반적인 방법은 for 루프를 사용하는 것입니다. 파이썬에서는 리스트의 각 요소를 읽고, 쓰고, 업데이트할 수 있는 여러 방법을 제공합니다.

  • 리스트 요소 읽기: for 루프를 사용하여 리스트의 각 요소를 순서대로 읽을 수 있습니다.
>>> cheeses = ['Cheddar', 'Edam', 'Gouda']
>>> for cheese in cheeses:
...     print(cheese)
Cheddar
Edam
Gouda
  • 리스트 요소 업데이트: 리스트의 요소를 업데이트하려면 인덱스를 사용해야 합니다. rangelen 함수를 함께 사용하여 리스트의 인덱스를 생성하고, 이를 사용하여 각 요소를 수정할 수 있습니다.
>>> numbers = [17, 123, 45, 67]
>>> for i in range(len(numbers)):
...     numbers[i] = numbers[i] * 2
>>> numbers
[34, 246, 90, 134]
  • 빈 리스트 순회: 빈 리스트를 순회하려고 하면, for 루프의 본문이 실행되지 않습니다.
>>> empty = []
>>> for x in empty:
...     print('This never happens.')

4. 리스트 연산 (List operations)

리스트는 여러 가지 연산을 통해 조작할 수 있습니다. 그 중 가장 기본적인 연산으로는 + 연산자와 * 연산자가 있습니다.

  • 리스트 합치기 (+ 연산): 두 리스트를 합쳐서 새로운 리스트를 만들 수 있습니다.
# + 연산
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print(c)
[1, 2, 3, 4, 5, 6]

이 예제에서 ab 리스트를 + 연산자를 사용하여 합치면, 두 리스트의 요소들이 순서대로 결합된 새로운 리스트 c가 생성됩니다.

  • 리스트 반복 (* 연산): 리스트를 여러 번 반복하여 새로운 리스트를 만들 수 있습니다.
# * 연산
>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

첫 번째 예제에서는 [0] * 4[0, 0, 0, 0]이라는 결과를 생성합니다. 두 번째 예제에서는 [1, 2, 3] * 3[1, 2, 3, 1, 2, 3, 1, 2, 3]이라는 결과를 생성합니다. 이는 리스트의 요소들이 지정된 횟수만큼 반복된 새로운 리스트를 만듭니다.

5. 리스트 슬라이싱 (List slices)

리스트 슬라이싱은 리스트의 부분 집합을 추출하거나 여러 요소를 한 번에 수정하는 강력한 도구입니다. 슬라이싱 구문은 문자열 슬라이싱과 유사하게 작동합니다.

  • 리스트의 부분 추출: 슬라이싱을 사용하여 리스트의 특정 부분을 추출할 수 있습니다. 슬라이싱 구문은 [시작:끝] 형식을 사용하며, 시작 인덱스부터 끝 인덱스 직전까지의 요소들을 포함합니다.
>>> t = ['a', 'b', 'c', 'd', 'e', 'f']
>>> t[1:3]
['b', 'c']
>>> t[:4]
['a', 'b', 'c', 'd']
>>> t[3:]
['d', 'e', 'f']
  • t[1:3]: 인덱스 1부터 3 직전까지의 요소를 추출합니다. 결과는 ['b', 'c']입니다.
  • t[:4]: 리스트의 시작부터 인덱스 4 직전까지의 요소를 추출합니다. 결과는 ['a', 'b', 'c', 'd']입니다.
  • t[3:]: 인덱스 3부터 리스트의 끝까지의 요소를 추출합니다. 결과는 ['d', 'e', 'f']입니다.
  • 리스트 전체 복사: 슬라이싱 구문에서 시작과 끝 인덱스를 모두 생략하면, 리스트의 전체 복사본을 얻을 수 있습니다.
>>> t[:]
['a', 'b', 'c', 'd', 'e', 'f']
  • t[1:3] = ['x', 'y']: 인덱스 1부터 3 직전까지의 요소를 ‘x’와 ‘y’로 대체합니다. 결과적으로 리스트 t['a', 'x', 'y', 'd', 'e', 'f']로 변경됩니다.

6. 리스트 메서드 (List methods)

리스트는 다양한 내장 메서드를 제공하여 요소를 추가, 확장 및 정렬하는 등의 작업을 쉽게 수행할 수 있습니다.

  • append() 메서드: 리스트의 끝에 새로운 요소를 추가합니다.
>>> t = ['a', 'b', 'c']
>>> t.append('d')
>>> print(t)
['a', 'b', 'c', 'd']
  • extend() 메서드: 다른 리스트의 모든 요소를 기존 리스트의 끝에 추가합니다.
>>> t1 = ['a', 'b', 'c']
>>> t2 = ['d', 'e']
>>> t1.extend(t2)
>>> print(t1)
['a', 'b', 'c', 'd', 'e']
  • sort() 메서드: 리스트의 요소를 오름차순으로 정렬합니다.
>>> t = ['d', 'c', 'e', 'b', 'a']
>>> t.sort()
>>> print(t)
['a', 'b', 'c', 'd', 'e']

7. 리스트 요소 삭제 (Deleting elements)

리스트에서 요소를 삭제하는 여러 가지 방법이 있습니다. 각각의 방법은 특정 상황에 유용하게 사용될 수 있습니다.

  • pop() 메서드: 리스트에서 지정된 인덱스의 요소를 제거하고, 그 요소를 반환합니다. 인덱스를 지정하지 않으면 리스트의 마지막 요소를 제거합니다.
>>> t = ['a', 'b', 'c']
>>> x = t.pop(1)
>>> print(t)
['a', 'c']
>>> print(x)
b

>>> a = [1, 2, 3, 4, 5]
>>> a.pop()
5
>>> a
[1, 2, 3, 4]
  • del 문: 인덱스를 사용하여 리스트의 특정 요소를 삭제합니다. 슬라이싱을 사용하여 여러 요소를 한 번에 삭제할 수도 있습니다.
>>> t = ['a', 'b', 'c']
>>> del t[1]
>>> print(t)
['a', 'c']

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del t[1:5]
>>> print(t)
['a', 'f']
  • remove() 메서드: 리스트에서 지정된 값을 찾아 첫 번째로 발견된 요소를 삭제합니다.
>>> t = ['a', 'b', 'c']
>>> t.remove('b')
>>> print(t)
['a', 'c']

8. 리스트와 함수 (Lists and functions)

리스트 객체는 다양한 내장 함수와 함께 사용될 수 있어 데이터를 쉽게 처리하고 분석할 수 있습니다.

  • (실습#1) 리스트에서 사용 가능한 빌트인 함수들
>>> nums = [3, 41, 12, 9, 74, 15]
>>> print(len(nums))
6
>>> print(max(nums))
74
>>> print(min(nums))
3
>>> print(sum(nums))
154
>>> print(sum(nums)/len(nums))
25
  • len(nums): 리스트의 요소 개수를 반환합니다. 결과는 6입니다.
  • max(nums): 리스트에서 가장 큰 값을 반환합니다. 결과는 74입니다.
  • min(nums): 리스트에서 가장 작은 값을 반환합니다. 결과는 3입니다.
  • sum(nums): 리스트의 모든 요소의 합을 반환합니다. 결과는 154입니다.
  • sum(nums)/len(nums): 리스트 요소의 평균 값을 계산합니다. 결과는 25입니다.
  • (실습#2) 리스트를 사용하여 평균 계산 (리스트를 사용하지 않은 경우와 리스트를 사용한 경우의 평균 계산 방법을 비교합니다.)
# 리스트를 사용하지 않은 경우 (avenum.py)
total = 0
count = 0
while (True):
    inp = input('Enter a number: ')
    if inp == 'done': break
    value = float(inp)
    total = total + value
    count = count + 1

average = total / count
print('Average:', average)
  • 사용자가 숫자를 입력하고, ‘done’을 입력할 때까지 반복합니다.
  • 입력된 숫자를 모두 더하고 개수를 셉니다.
  • 마지막에 총합을 개수로 나누어 평균을 계산합니다.
# 리스트를 사용한 경우 (avelist.py)
numlist = list()
while (True):
    inp = input('Enter a number: ')
    if inp == 'done': break
    value = float(inp)
    numlist.append(value)

average = sum(numlist) / len(numlist)
print('Average:', average)
  • 빈 리스트를 생성합니다.
  • 사용자가 숫자를 입력하고, ‘done’을 입력할 때까지 반복합니다.
  • 입력된 숫자를 리스트에 추가합니다.
  • 마지막에 리스트의 합을 리스트의 길이로 나누어 평균을 계산합니다.

리스트를 사용하면 데이터를 저장하고 필요할 때 쉽게 접근할 수 있어 코드가 더 간결하고 이해하기 쉬워집니다. 또한, sum()len() 같은 내장 함수를 사용하여 평균을 계산하는 것이 더 직관적입니다.

9. 리스트와 문자열 (Lists and strings)

리스트와 문자열은 상호 변환이 가능하며, 이를 통해 문자열을 다루는 다양한 방법을 배울 수 있습니다.

  • (실습#1) 문자열을 리스트로 변환
>>> s = 'spam'
>>> t = list(s)
>>> print(t)
['s', 'p', 'a', 'm']
  • list(s): 문자열 s를 문자 단위로 나누어 리스트로 변환합니다. 결과는 ['s', 'p', 'a', 'm']입니다.
  • (실습#2) 문자열을 단어 단위로 분할
>>> s = 'pining for the fjords'
>>> t = s.split()
>>> print(t)
['pining', 'for', 'the', 'fjords']
>>> print(t[2])
the
  • s.split(): 문자열 s를 공백 단위로 나누어 리스트로 변환합니다. 결과는 ['pining', 'for', 'the', 'fjords']입니다.
  • t[2]: 리스트 t의 세 번째 요소를 출력합니다. 결과는 the입니다.
  • (실습#3) 특정 구분자를 사용하여 문자열 분할
>>> s = 'spam-spam-spam'
>>> delimiter = '-'
>>> s.split(delimiter)
['spam', 'spam', 'spam']
  • s.split(delimiter): 문자열 s를 지정된 구분자 delimiter를 기준으로 나누어 리스트로 변환합니다. 결과는 ['spam', 'spam', 'spam']입니다.
  • (실습#4) 리스트를 문자열로 결합
>>> t = ['pining', 'for', 'the', 'fjords']
>>> delimiter = ' '
>>> delimiter.join(t)
'pining for the fjords'
  • delimiter.join(t): 리스트 t의 요소들을 지정된 구분자 delimiter를 사용하여 하나의 문자열로 결합합니다. 결과는 'pining for the fjords'입니다.

10. 라인 파싱 (Parsing lines)

라인 파싱은 파일에서 특정 조건을 만족하는 라인을 찾고, 그 라인에서 필요한 정보를 추출하는 작업입니다. 아래 예제는 파일의 각 라인을 파싱하여 특정 조건을 만족하는 라인에서 필요한 정보를 추출하는 방법을 보여줍니다.

  • (실습)
# search5.py

with open('mbox-short.txt', 'r') as f:
    for line in f:
        line = line.rstrip()
        if not line.startswith('From '): continue
        words = line.split()
        print(words[2])

이 코드의 목적은 mbox-short.txt 파일을 읽고, “From “으로 시작하는 라인에서 요일 정보를 추출하는 것입니다.

  1. with open('mbox-short.txt', 'r') as f:: 파일을 읽기 모드('r')로 열고, 파일 객체를 f에 할당합니다. with 문을 사용하면 파일을 자동으로 닫아줍니다.
  2. for line in f:: 파일의 각 라인을 순회합니다.
  3. line = line.rstrip(): 현재 라인의 오른쪽 끝 공백 문자를 제거합니다.
  4. if not line.startswith('From '): continue: 현재 라인이 “From “으로 시작하지 않으면, 다음 라인으로 건너뜁니다.
  5. words = line.split(): 라인을 공백을 기준으로 분할하여 단어 리스트로 만듭니다.
  6. print(words[2]): 분할된 단어 리스트에서 세 번째 단어(요일)를 출력합니다.

11. 객체와 값 (Objects and values)

파이썬에서 객체와 값은 중요한 개념입니다. 객체는 메모리 상의 위치를 가지며, 값은 그 객체의 내용입니다. 두 변수가 동일한 객체를 참조하는지 여부를 확인할 때 is 연산자를 사용합니다.

  • (실습#1)
>>> a = 'banana'
>>> b = 'banana'
>>> a is b
True
>>> id(a), id(b)
  • ab는 동일한 문자열 ‘banana’를 참조합니다.
  • a is b: 두 변수가 동일한 객체를 참조하는지 확인합니다. 이 경우 True를 반환합니다. 이는 파이썬이 같은 문자열 리터럴을 동일한 객체로 처리하기 때문입니다.
  • id(a), id(b): ab의 객체 ID를 확인합니다. 동일한 문자열 리터럴이기 때문에 id(a)id(b)는 같습니다.
  • (실습#2)
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
  • ab는 동일한 값을 가지는 리스트이지만, 서로 다른 객체를 참조합니다.
  • a is b: 두 변수가 동일한 객체를 참조하는지 확인합니다. 이 경우 False를 반환합니다. 이는 파이썬이 새로운 리스트 객체를 각각 생성하기 때문입니다.

12. 별칭 (Aliasing)

별칭은 두 개 이상의 변수가 동일한 객체를 참조하는 상황을 말합니다. 이를 통해 동일한 데이터를 여러 이름으로 다룰 수 있지만, 주의하지 않으면 의도하지 않은 부작용이 발생할 수 있습니다.

  • (실습)
>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

>>> b[0] = 17
>>> print(a)
[17, 2, 3]
  • a = [1, 2, 3]: 리스트 [1, 2, 3]를 변수 a에 할당합니다.
  • b = a: 변수 ba를 할당합니다. 이제 ba는 동일한 리스트 객체를 참조합니다.
  • b is a: ba가 동일한 객체를 참조하는지 확인합니다. 결과는 True입니다.
  • b[0] = 17: 리스트 b의 첫 번째 요소를 17로 변경합니다. ba는 동일한 객체를 참조하므로, a의 첫 번째 요소도 17로 변경됩니다.
  • print(a): a의 내용을 출력합니다. 결과는 [17, 2, 3]입니다.

13. 리스트 인자 (List arguments)

리스트를 함수의 인자로 전달할 때 함수가 리스트를 어떻게 처리하는지 이해하는 것이 중요합니다. 리스트는 변경 가능한 객체이므로, 함수 내에서 리스트를 수정하면 원래 리스트도 변경됩니다.

  • (실습#1)
>>> def delete_head(t):
...     del t[0]
...
>>> letters = ['a', 'b', 'c']
>>> delete_head(letters)
>>> print(letters)
['b', 'c']
  • delete_head(t): 리스트 t의 첫 번째 요소를 삭제합니다.
  • letters 리스트를 함수에 전달하면, 함수 내에서 첫 번째 요소가 삭제됩니다. 결과적으로 letters['b', 'c']가 됩니다.
>>> t1 = [1, 2]
>>> t2 = t1.append(3)
>>> print(t1)
[1, 2, 3]
>>> print(t2)
None
  • t1.append(3): 리스트 t1의 끝에 3을 추가합니다. append 메서드는 리스트를 수정하고 None을 반환합니다. 따라서 t2None이 됩니다.
  • 결과적으로 t1[1, 2, 3]이 되고, t2None이 됩니다.
>>> t3 = t1 + [3]
>>> print(t3)
[1, 2, 3, 3]
>>> t1 is t3
False
  • t1 + [3]: 리스트 t1[3]을 더하여 새로운 리스트 t3를 생성합니다.
  • t1t3는 서로 다른 객체입니다. 따라서 t1 is t3False를 반환합니다.
  • (실습#2)
# 실행결과를 예상해보세요.
def delete_head(t):
    t[1:]

letters = ['a', 'b', 'c']
delete_head(letters)
print(letters)
  • delete_head(t) 함수는 t[1:]을 반환하지 않고 슬라이싱 결과를 무시합니다. 따라서 letters 리스트는 변경되지 않습니다.
  • 결과적으로 letters는 여전히 ['a', 'b', 'c']입니다.
  • 아래는 수정된 코드입니다.
def delete_head(t):
    return t[1:]

letters = ['a', 'b', 'c']
rest = delete_head(letters)
print(rest)
  • 수정된 함수는 t[1:]을 반환합니다. 이제 delete_head 함수는 리스트의 첫 번째 요소를 제외한 나머지 요소를 반환합니다.
  • rest['b', 'c']가 되며, 이를 출력하면 ['b', 'c']가 출력됩니다.

Exercises

(1) 파일에서 모든 고유 단어 찾기

  • 텍스트 파일 https://www.py4e.com/code3/romeo.txt 를 다운로드 받습니다.
  • 파일에서 모든 고유 단어를 찾아 리스트에 저장합니다.
  • 고유 단어를 알파벳 순으로 정렬하여 출력합니다.
Enter file: romeo.txt
['Arise', 'But', 'It', 'Juliet', 'Who', 'already', 'and', 'breaks', 'east', 'envious', 'fair', 'grief', 'is', 'kill', 'light', 'moon', 'pale', 'sick', 'soft', 'sun', 'the', 'through', 'what', 'window', 'with', 'yonder']
  • (정답)
file_name = input('Enter file: ')

words = []
with open(file_name, 'r') as f:
    for line in f:
        line = line.strip()
        items = line.split()
        for item in items:
            if item not in words:
                words.append(item)

words.sort()
print(words)

(2) 이메일 주소 추출하기

  • 텍스트 파일에서 “From “으로 시작하는 라인을 찾습니다.
  • 해당 라인에서 메일 주소를 추출하여 출력합니다.
  • “From “으로 시작하는 라인의 총 개수를 출력합니다.
$ python fromcount.py
Enter a file name: mbox-short.txt
stephen.marquard@uct.ac.za
louis@media.berkeley.edu
zqian@umich.edu
[...some output removed...]
ray@media.berkeley.edu
cwen@iupui.edu
cwen@iupui.edu
cwen@iupui.edu
There were 27 lines in the file with From as the first word
  • (정답)
# fromcount.py
file_name = input('Enter a file name: ')

count = 0
with open(file_name, 'r') as f:
    for line in f:
        line = line.strip()
        if line.startswith('From '):
            email = line.split()[1]
            print(email)
            count += 1

print(f'There were {count} lines in the file with From as the first word')

(3) 사용자 입력을 받아 최대값과 최소값 출력하기

  • 사용자로부터 숫자를 입력받습니다.
  • ‘done’을 입력하면 입력을 종료하고, 최대값과 최소값을 출력합니다.
  • 입력받은 숫자들은 리스트에 저장됩니다.
  • 잘못된 입력에 대해서는 예외 처리를 통해 ‘Invalid input’ 메시지를 출력합니다.
Enter a number: 6
Enter a number: 2
Enter a number: 9
Enter a number: 3
Enter a number: 5
Enter a number: done
Maximum: 9.0
Minimum: 2.0
  • (정답)
numbers = []

while True:
    num = input('Enter a number: ')
    if num == 'done':
        break
    try:
        num = float(num)
        numbers.append(num)
    except ValueError:
        print('Invalid input')

if numbers:  # 리스트가 비어 있지 않은 경우에만 출력
    print(f'Maximum: {max(numbers)}')
    print(f'Minimum: {min(numbers)}')
else:
    print('No valid numbers were entered.')

파이썬 기초 강좌: 9. 딕셔너리 예제와 실습 (Python Basics – Dictionary Examples and Exercises)

Leave a Comment