티스토리 뷰
for반복문과 range함수
for 반복문 기초
for문을 배우기 전에 while 반복문을 써서 리스트의 모든 원소를 출력해보겠습니다.
# 빅뱅 멤버들
big_bang = ["지드래곤", "태양", "탑", "대성", "승리"]
i = 0
while i < len(big_bang):
print(big_bang[i])
i = i + 1
지드래곤
태양
탑
대성
승리
위 프로그램에서는 몇 가지 불필요한 점들이 있습니다. 첫째로, i라는 변수는 인덱싱을 위한 용도일 뿐, 그 이외에는 아무런 쓸모가 없습니다. 둘째로, len(big_bang)도 i와의 비교 이외에는 별도의 쓰임이 없습니다.
for문을 쓰면 이런 불필요한 점들 없이, 깔끔하고 직관적이게 코드를 짤 수 있습니다.
# 빅뱅 멤버들
big_bang = ["지드래곤", "태양", "탑", "대성", "승리"]
for member in big_bang:
print(member)
지드래곤
태양
탑
대성
승리
위의 코드에서 member는 for문의 수행부분에서만 쓰이고 사라지는 local 변수입니다. 수행부분으로 처음 들어갈 때는 member가 big_bang 리스트의 0번 인덱스 요소 "지드래곤"을 갖게 됩니다. 그 다음 들어갈 때는 member가 1번 인덱스의 요소 "태양", 그 다음은 2번 인덱스 요소 "탑", 3번 인덱스 요소 "대성", 그리고 마지막으로 4번 인덱스 요소 "승리"를 갖게 됩니다. 결과적으로 "지드래곤", "태양", "탑", "대성", "승리"가 출력됩니다.
여기서 쓴 member는 제가 임의로 정한 이름으로, x나 i 등 어떤 (허용된) 이름을 붙이더라도 문제 없이 프로그램이 실행됩니다. 하지만 늘 강조하듯이 member같은 의미 있는 이름을 주는 것이 좋습니다.
지금까지 while문과 for문을 사용하여 빅뱅 맴버들을 출력해보았습니다. 이처럼 while 반복문으로 만들 수 있는 프로그램은 for 반복문으로도 만들 수 있고, 반대로 for 반복문으로 만들 수 있는 프로그램은 모두 while문으로도 만들 수 있습니다. 따라서 이 파트를 배운다고 해서 새로운 문제 해결 능력이 길러지는 것은 아닙니다. 하지만 for문을 잘 활용하면, 코드를 훨씬 읽고 쓰기 쉽게 만들 수 있습니다. 이와 같이 프로그래밍 언어에서 동일한 기능을 깔끔하게 만들어 놓은 것을 'syntactic sugar'(꿀)라고 부릅니다.
또 하나의 예시를 볼까요? 다음은 [1, 3, 5, 7, 9]의 각 원소의 제곱을 출력해주는 프로그램입니다.
for num in [1, 3, 5, 7, 9]:
print(num * num)
1
9
25
49
81
이 경우 while문 보다 for문이 훨씬 더 짧고 깔끔하고 직관적이죠? 이처럼 for문은 리스트를 다루는 데에 최적화되어 있습니다.
위 두 개의 코드를 일반화하면, for문의 기본 구조는 다음과 같습니다:
for 변수 in 리스트/range/문자열:
<첫번째 실행할 줄>
<두번째 실행할 줄>
...
range 함수
for문을 사용하여 1부터 10까지 출력하려면, 어떻게 해야할까요?
for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
print(i)
1
2
3
4
5
6
7
8
9
10
그런데 1부터 100까지 출력하려면, 어떻게 해야할까요? 이를 위해 1부터 100까지 적혀있는 리스트를 만드는 것은 바보같은 일이겠죠?
range 함수를 쓰면 이 문제를 간단하게 해결할 수 있습니다.
파라미터가 2개 있는 range 함수
range(n, m)은 n부터 m - 1까지의 수들을 의미합니다.
for i in range(n, m):
print(i)
for문에서 range(1, 11)이라는 코드를 쓰면, 1부터 10까지의 수이기 때문에, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]이라는 리스트를 쓰는 것과 동일한 효과를 얻게 됩니다.
for i in range(1, 11):
print(i)
1
2
3
4
5
6
7
8
9
10
파라미터가 1개 있는 range 함수
range(m)은 0부터 m - 1까지의 수들을 의미합니다.
for i in range(m):
print(i)
for문에서 range(10)이라는 코드를 쓰면, 0부터 9까지의 수이기 때문에, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]라는 리스트를 쓰는 것과 동일한 효과를 얻게 됩니다.
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
파라미터가 3개 있는 range 함수
range(n, m, s)은 n부터 m - 1까지의 수 중 간격이 s인 수들을 의미합니다.
for i in range(n, m, s):
print(i)
for문에서 range(3, 17, 3)이라는 코드를 쓰면, 3부터 16까지의 수 중 간격이 3인 수이기 때문에, [3, 6, 9, 12, 15]를 쓰는 것과 동일한 효과를 얻게 됩니다.
for i in range(3, 17, 3):
print(i)
3
6
9
12
15
for문에서 range(3, 16, 3)이라는 코드를 쓰면, 3부터 15까지의 수 중 간격이 3인 수이기 때문에, [3, 6, 9, 12, 15]라는 리스트를 쓰는 것과 동일한 효과를 얻게 됩니다.
for i in range(3, 16, 3):
print(i)
3
6
9
12
15
for문에서 range(3, 15, 3)이라는 코드를 쓰면, 3부터 14까지의 수 중 간격이 3인 수이기 때문에, [3, 6, 9, 12]라는 리스트를 쓰는 것과 동일한 효과를 얻게 됩니다. 이전에 리스트에 포함되어 있었던 15는 3부터 14 안의 범위에 들어있지 않기 때문에 빠져 있습니다.
for i in range(3, 15, 3):
print(i)
3
6
9
12
range 함수의 장점은 다음과 같습니다:
- 간편하고 깔끔합니다. 굳이 리스트를 만들지 않아도, 동일한 효과를 낼 수 있습니다.
- 메모리가 효율적입니다.
1부터100까지의 리스트를 쓰면 파이썬에서 그만큼의 공간을 마련해야 하는 반면,range함수를 쓰면1의 값을 쓰고 버리고2의 값을 쓰고 버리고 하는 식으로 메모리 공간을 아낄 수 있습니다.
Aliasing
아래 프로그램을 실행하면 어떤 결과값이 나올까요?
x = 5
y = x
y = 3
print(x)
print(y)
5
3
첫 번째 줄에서는 정수 5가 변수 x에 지정됩니다. 그리고 두 번째 줄에서는 x의 값이 현재 5이기 때문에, 정수 5가 변수 y에 지정됩니다. 세 번째 줄에서는 y의 값이 3으로 바뀝니다. 따라서 이를 실행하면 5와 3이 차례로 출력됩니다.
엘리어싱 (Aliasing)
그럼 이 프로그램을 실행하면 어떠한 결과값이 나올까요?
x = [2, 3, 5, 7, 11]
y = x
y[2] = 4
print(x)
print(y)
[2, 3, 4, 7, 11]
[2, 3, 4, 7, 11]
앞서 본 변수의 예처럼 x는 [2, 3, 5, 7, 11]이 나오고, y는 [2, 3, 4, 7, 11]이 나올줄 알았는데, 결과가 예상과 다르죠? y의 원소만 바꾸었을 뿐인데, 왜 x와 y가 똑같이 [2, 3, 4, 7, 11]로 변했을까요?
엘리어싱(Aliasing)이라는 개념 때문입니다. 코드의 첫 번째 줄에서는 [2, 3, 5, 7, 11] 리스트에 x라는 이름표를 달아줍니다. 두 번째 줄에서 y = x라는 명령에 의해, 그 같은 리스트에 y라는 이름표를 달아줍니다. 세 번째 줄에서 리스트 y의 인덱스 2의 값을 4로 바꿔주면, 리스트는 [2, 3, 4, 7, 11]이 됩니다.
그런데 바뀐 [2, 3, 4, 7, 11]이라는 리스트가 y라는 이름표 뿐만 아니라, x라는 이름표도 갖고 있었죠? 그래서 x와 y를 출력하라는 명령이 있을 때, 모두 같은 [2, 3, 4, 7, 11]이 출력되는 것입니다. 두 변수가 서로 이름은 다르지만, 사실 같은 메모리 주소에 있는 값을 가리키고 있기 때문에 동일한 리스트가 출력된거죠. y는 x의 Alias(가명)라고 얘기할 수 있습니다.
리스트 복사
하지만 간혹 엘리어싱을 이용하지 않고 리스트의 원본을 그대로 둔 채, 리스트의 복사본만 바꾸고 싶을 때가 있습니다. 즉 x라는 원본의 리스트는 그대로 둔 채, y 리스트의 값만 바꾸고 싶은 경우. 이럴 때는 list 함수를 사용하면 됩니다. list 함수는 원본을 복사해서 새로운 공간에 붙여넣고, 그 새로운 공간의 리스트를 리턴시켜줍니다.
x = [2, 3, 5, 7, 11]
y = list(x)
y[2] = 4
print(x)
print(y)
[2, 3, 5, 7, 11]
[2, 3, 4, 7, 11]
첫 번째 줄에서 [2, 3, 5, 7, 11]을 가진 리스트에 x라는 이름표가 달립니다. 두 번째 줄에서 y = list(x)라는 명령에 의해, x 리스트의 복사본이 새로운 공간에 만들어지고 그 복사본에 y라는 이름표가 달립니다. 즉 x와 y의 리스트는 서로 다른 메모리 주소에 저장되어 있는거죠.
따라서 세 번째 줄에서 y 리스트의 인덱스 2의 값을 바꾸는 행위는, x 리스트에 아무런 영향을 미치지 않습니다. 이로써 x와 y를 출력하라는 명령이 있을 때, 각각 [2, 3, 5, 7, 11]과 [2, 3, 4, 7, 11]이 출력되는 것입니다.
리스트와 문자열
리스트와 문자열은 굉장히 비슷합니다. 리스트가 어떤 자료형들의 나열이라면, 문자열은 문자들의 나열이라고 할 수 있겠죠. 지금부터 파이썬에서 리스트와 문자열이 어떻게 같고 어떻게 다른지 알아봅시다.
인덱싱 (Indexing)
두 자료형은 공통적으로 인덱싱이 가능합니다.
# 알파벳 리스트의 인덱싱
alphabets_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
print(alphabets_list[0])
print(alphabets_list[1])
print(alphabets_list[4])
print(alphabets_list[-1])
# 알파벳 문자열의 인덱싱
alphabets_string = 'ABCDEFGHIJ'
print(alphabets_string[0])
print(alphabets_string[1])
print(alphabets_string[4])
print(alphabets_string[-1])
A
B
E
J
A
B
E
J
슬라이싱 (Slicing)
두 자료형은 공통적으로 슬라이싱이 가능합니다.
# 알파벳 리스트의 슬라이싱
alphabets_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
print(alphabets_list[0:5])
print(alphabets_list[4:])
print(alphabets_list[:4])
# 알파벳 문자열의 슬라이싱
alphabets_string = 'ABCDEFGHIJ'
print(alphabets_string[0:5])
print(alphabets_string[4:])
print(alphabets_string[:4])
['A', 'B', 'C', 'D', 'E']
['E', 'F', 'G', 'H', 'I', 'J']
['A', 'B', 'C', 'D']
ABCDE
EFGHIJ
ABCD
덧셈 연산
두 자료형에게 모두 덧셈은 "연결"하는 연산입니다.
# 리스트의 덧셈 연산
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
list3 = list1 + list2
print(list3)
# 문자열의 덧셈 연산
string1 = '1234'
string2 = '5678'
string3 = string1 + string2
print(string3)
[1, 2, 3, 4, 5, 6, 7, 8]
12345678
len 함수
두 자료형은 모두 길이를 재는 len 함수를 쓸 수 있습니다.
# 리스트의 길이 재기
print(len(['H', 'E', 'L', 'L', 'O']))
# 문자열의 길이 재기
print(len("Hello, world!"))
5
13
Mutable (수정 가능) vs. Immutable (수정 불가능)
하지만 차이점이 있습니다. 리스트는 데이터를 바꿀 수 있지만, 문자열은 데이터를 바꿀 수 없다는 것입니다. 리스트와 같이 수정 가능한 자료형을 'mutable'한 자료형이라고 부르고, 문자열과 같이 수정 불가능한 자료형을 'immutable'한 자료형이라고 부릅니다. 숫자, 불린, 문자열은 모두 immutable한 자료형입니다.
# 리스트 데이터 바꾸기
numbers = [1, 2, 3, 4]
numbers[0] = 5
print(numbers)
[5, 2, 3, 4]
리스트 numbers의 인덱스 0에 5를 새롭게 지정해주었습니다. [5, 2, 3, 4]가 출력되었습니다. 이처럼 리스트는 데이터의 생성, 삭제, 수정이 가능합니다.
# 문자열 데이터 바꾸기
name = "codeit"
name[0] = "C"
print(name)
Traceback (most recent call last):
File "untitled.py", line 3, in <module>
name[0] = "C"
TypeError: 'str' object does not support item assignment
문자열 name의 인덱스 0 에 "C"를 새롭게 지정해주었더니 오류가 나왔습니다. TypeError: 'str' object does not support item assignment는 문자열은 변형이 불가능하다는 메시지입니다. 이처럼 문자열은 리스트와 달리 데이터의 생성, 삭제, 수정이 불가능합니다.
