카테고리 없음

파이썬 쿡북.

도민혁 2023. 4. 7. 10:27

파이썬이란?

파이썬은 1991년에 발표된 인터프리터 방식의 프로그래밍 언어이다. 파이썬의 강력한 라이브러리와 풍부한 생태계를 통해, 데이터를 수집하고 분석하며 시각화할 수 있다. 데이터 분석 분야에서 파이썬의 사용이 널리 퍼진 이유 중 하나는, 다른 프로그래밍 언어에 비해 비교적 쉽고 간편하게 사용할 수 있기 때문이다.

 

 

 

 

 

파이썬에서 문자를 출력하는 방법

Python에서 무언가를 출력하려면 print() 함수를 사용합니다.

print를 쓰고 뒤에 출력할()를 넣습니다.

 

 

 

 

 

문자열 출력 방법


(출력할 문자에 ""를 넣는다)

print("Hello world")

 

 

 

 

 

정수 출력 방법

print(7)

 

 

 

파이썬 기본 사칙연산

덧셈 ( + )
뺄셈 ( - )
곱셈 ( * )
나눗셈 ( / )
거듭제곱 ( ** )
몫 ( // )
나머지 ( % )

 

 

 

 

 

 

 

 

변수란?

파이썬에서 사용하는 변수는 객체를 가리키는 것이라고도 말할 수 있다. 

 

대표 역할

지정 기능을 실행하는 단위

코드의 가독성과 프로그램의 효율성 증대

 

a, b = 10, 30
print(a, b)

 

변수 a,b를 10, 30으로 설정한 후

print를 사용해 a와 b를 출력할 수 있다.

 

 

 

 

a = 1		
b = 1.5	
print(type(a))
print(type(b))

type 함수는 데이터의 자료형을 확인할 때 사용하는 함수이다.

 

실행결과는 각각 int형, float형이라고 뜬다.

 

 

a = "do"
b = "minhyuck"
c = "10" 
d = 10 
e = b

result = a + b #a와 d가 서로 다른 자료형이므로 result = a + d는 불가능하다.
print(a, b, c, d, e, result)

작은 따옴표('')와 큰 따옴표("")는 문자열을 표현하기 위한 장치이기 때문에 어떤 글자라고 따옴표안에 입력된 것은 문자열로 판단된다.

 

 

또한 변수는 초기화 가능하다.

 

 

 

 

 

 

 

 

 

 

 

파이썬 산술 연산자

산술연산자에는 사칙연산자 +, -, *, / 와 제곱을 나타내는 **, 나머지를 산출하는 % (Modulus), 그리고 나누기에 소숫점 이하를 버리는 // 연산자(Floor Division) 등이 있다.

+ 더하기
a + b = 30
- 빼기
a – b = -10
* 곱하기
a * b = 200
/ 나누기
b / a = 2
** 제곱
a = 3; b = 4; print(a ** b) 81
// 정수 몫
9//2 = 4 and 9.0//2.0 = 4.0, -11//3 = -4, -11.0//3 = -4.0
% 나머지
b % a = 0

 

파이썬 비교 연산자

비교연산자는 관계연산자로도 불리우는데, 여기에는 등호(==), 같지 않음(!=), 부등호(<, >, <=, >=) 등이 있다.
 
==
(a == b) is not true.
!=
(a != b) is true.
<>
(a <> b) is true. This is similar to != operator.
>
(a > b) is not true.
<
(a < b) is true.
>=
(a >= b) is not true.
<=
(a <= b) is true.

 

파이썬 논리 연산자

 

논리연산자에는 and, or, not 이 있는데, and 는 양쪽의 값이 모두 참인 경우만 참이 되고, or 는 어느 한쪽만 참이면 참이 된다. not 은 참이면 거짓으로 거짓이면 참이 된다. 아래 예제는 No가 출력된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

파이썬 입력함수

파이썬은 기본적으로 input()함수를 이용해서 사용자의 입력을 받는다. input은 입력되는 모든 것을 문자열로 취급한다.

변수에 값을 입력받을 때에는 a = input() 와 같은 형태로 이용한다.

a = input() # 1234ab 입력
print(a)

 

 

사용자 입력을 받을 때 메시지를 띄워줄 수도 있다. 예를 들어서 번호를 입력할 때 "번호를 입력하세요: " 와 같은 문구를 띄워주고 싶을 때가 있다. 이런 경우에 메시지를 input함수 내에 넣어주면 값을 입력받을 때 메시지를 띄워준다. 

a = input("값을 입력하세요 : ")

 

 

 

파이썬 여러 값 한 번에 입력받기 (split, map)

만약 입력받는 값의 수가 매우 많아지는 경우에는 이를 하나씩 받기에는 무리가 있다. 여러 값을 한 번에 어떻게 입력을 받을까?

split함수와 map함수를 이용하면 가능하다.

 

split함수는 이전의 문자열 자료형 게시글에서 설명하였듯이, 특정 문자를 기준으로 문자열을 잘라 리스트로 만들어주는 함수이다.

map은 map(변환 함수, 반복가능한 객체) 이렇게 두 인수를 받는다. 함수부분에는 원하는 자료형(int, float 등)을 넣어주면 된다.

map함수는 map타입의 객체를 반환하기 때문에 이를 다시 list나 tuple등으로 변환을 시켜주어야 한다. 

a = map(int, input().split())

print(a) # <map object at 0x01D56750>

a = list(map(int, input().split()))

print(a) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

 

문자열이란?

문자열(String)이란 문자, 단어 등으로 구성된 문자들의 집합을 의미한다. 예를 들어 다음과 같은 것들이 문자열이다.

"Life is too short, You need Python"
"a"
"123"

위 문자열 예문을 보면 모두 큰따옴표(" ")로 둘러싸여 있다. "123은 숫자인데 왜 문자열이지?"라는 의문이 드는 독자도 있을 것이다. 따옴표로 둘러싸여 있으면 모두 문자열이라고 보면 된다.

문자열은 어떻게 만들고 사용할까?

위 예에서는 문자열을 만들 때 큰따옴표(" ")만을 사용했지만 이 외에도 문자열을 만드는 방법은 3가지가 더 있다. 파이썬에서 문자열을 만드는 방법은 총 4가지이다.

1. 큰따옴표(")로 양쪽 둘러싸기

"Hello World"

2. 작은따옴표(')로 양쪽 둘러싸기

'Python is fun'

3. 큰따옴표 3개를 연속(""")으로 써서 양쪽 둘러싸기

"""Life is too short, You need python"""

4. 작은따옴표 3개를 연속(''')으로 써서 양쪽 둘러싸기

'''Life is too short, You need python'''

 

 

 

이스케이프 코드란?

문자열 예제에서 여러 줄의 문장을 처리할 때 백슬래시 문자와 소문자 n을 조합한 \n 이스케이프 코드를 사용했다. 이스케이프 코드란 프로그래밍할 때 사용할 수 있도록 미리 정의해 둔 "문자 조합"이다. 주로 출력물을 보기 좋게 정렬하는 용도로 사용한다. 몇 가지 이스케이프 코드를 정리하면 다음과 같다.

코드설명

\n 문자열 안에서 줄을 바꿀 때 사용
\t 문자열 사이에 탭 간격을 줄 때 사용
\\ 문자 \를 그대로 표현할 때 사용
\' 작은따옴표(')를 그대로 표현할 때 사용
\" 큰따옴표(")를 그대로 표현할 때 사용
\r 캐리지 리턴(줄 바꿈 문자, 현재 커서를 가장 앞으로 이동)
\f 폼 피드(줄 바꿈 문자, 현재 커서를 다음 줄로 이동)
\a 벨 소리(출력할 때 PC 스피커에서 '삑' 소리가 난다)
\b 백 스페이스
\000 널 문자

 

 

 

문자열 연산하기

파이썬에서는 문자열을 더하거나 곱할 수 있다. 다른 언어에서는 쉽게 찾아볼 수 없는 재미있는 기능으로, 우리 생각을 그대로 반영해 주는 파이썬만의 장점이라고 할 수 있다. 문자열을 더하거나 곱하는 방법에 대해 알아보자.

문자열 더해서 연결하기(Concatenation)

>>> head = "Python"
>>> tail = " is fun!"
>>> head + tail
'Python is fun!'

위 소스 코드에서 세 번째 줄을 보자. 복잡하게 생각하지 말고 눈에 보이는 대로 생각해 보자. "Python"이라는 head 변수와 " is fun!"이라는 tail 변수를 더한 것이다. 결과는 'Python is fun!'이다. 즉 head와 tail 변수가 +에 의해 합쳐진 것이다.

직접 실행해 보고 결괏값이 제시한 것과 똑같이 나오는지 확인해 보자.

문자열 곱하기

>>> a = "python"
>>> a * 2
'pythonpython'

위 소스 코드에서 *의 의미는 우리가 일반적으로 사용하는 숫자 곱하기의 의미와는 다르다. 위 소스 코드에서 a * 2 문장은 a를 두 번 반복하라는 뜻이다. 즉 *는 문자열의 반복을 뜻하는 의미로 사용되었다. 굳이 코드의 의미를 설명할 필요가 없을 정도로 직관적이다.

문자열 곱하기 응용

문자열 곱하기를 좀 더 응용해 보자. 다음 소스를 IDLE 에디터를 열고 작성해 보자.

# multistring.py

print("=" * 50)
print("My Program")
print("=" * 50)

 

 

 

문자열 인덱싱과 슬라이싱

인덱싱(Indexing)이란 무엇인가를 "가리킨다"는 의미이고, 슬라이싱(Slicing)은 무엇인가를 "잘라낸다"는 의미이다. 이런 의미를 생각하면서 다음 내용을 살펴보자.

문자열 인덱싱이란?

>>> a = "Life is too short, You need Python"

위 소스 코드에서 변수 a에 저장한 문자열의 각 문자마다 번호를 매겨 보면 다음과 같다.

Life is too short, You need Python
0         1         2         3 
0123456789012345678901234567890123

"Life is too short, You need Python" 문자열에서 L은 첫 번째 자리를 뜻하는 숫자 0, 바로 다음인 i는 1 이런 식으로 계속 번호를 붙인 것이다. 중간에 있는 short의 s는 12가 된다.

이제 다음 예를 실행해 보자.

>>> a = "Life is too short, You need Python"
>>> a[3]
'e'

a[3]이 뜻하는 것은 a라는 문자열의 네 번째 문자 e를 말한다. 프로그래밍을 처음 접하는 독자라면 a[3]에서 숫자 3이 왜 네 번째 문자를 뜻하는지 의아할 수도 있다. 사실 이 부분이 헷갈릴 수 있는 부분인데, 이렇게 생각하면 쉽게 알 수 있을 것이다.

"파이썬은 0부터 숫자를 센다."

따라서 파이썬은 위 문자열을 다음과 같이 바라보고 있다.

a[0]:'L', a[1]:'i', a[2]:'f', a[3]:'e', a[4]:' ', ...

0부터 숫자를 센다는 것이 처음에는 익숙하지 않겠지만 계속 사용하다 보면 자연스러워질 것이다. 위 예에서 볼 수 있듯이 a[번호]는 문자열 안의 특정한 값을 뽑아내는 역할을 한다. 이러한 작업을 인덱싱이라고 한다.

 

 

문자열 슬라이싱이란?

그렇다면 "Life is too short, You need Python" 문자열에서 단순히 한 문자만을 뽑아내는 것이 아니라 'Life' 또는 'You' 같은 단어를 뽑아내는 방법은 없을까?

다음과 같이 하면 된다.

>>> a = "Life is too short, You need Python"
>>> b = a[0] + a[1] + a[2] + a[3]
>>> b
'Life'

위 방법처럼 단순하게 접근할 수도 있지만 파이썬에서는 더 좋은 방법을 제공한다. 바로 슬라이싱(Slicing) 기법이다.

인덱싱 기법과 슬라이싱 기법은 뒤에서 배울 자료형인 리스트나 튜플에서도 사용할 수 있다.

위 예는 슬라이싱 기법으로 다음과 같이 간단하게 처리할 수 있다.

>>> a = "Life is too short, You need Python"
>>> a[0:4]
'Life'

a[0:4]가 뜻하는 것은 a 문자열, 즉 "Life is too short, You need Python" 문장에서 자리 번호 0부터 4까지의 문자를 뽑아낸다는 뜻이다. 하지만 다음과 같은 의문이 생길 것이다. a[0]은 L, a[1]은 i, a[2]는 f, a[3]은 e니까 a[0:3]으로도 Life라는 단어를 뽑아낼 수 있지 않을까? 다음 예로 확인해 보자.

>>> a[0:3]
'Lif'

이렇게 되는 이유는 간단하다. 슬라이싱 기법으로 a[시작 번호:끝 번호]를 지정할 때 끝 번호에 해당하는 것은 포함하지 않기 때문이다. a[0:3]을 수식으로 나타내면 다음과 같다.

0 <= a < 3

이 수식을 만족하는 것은 a[0], a[1], a[2]이다. 따라서 a[0:3]은 'Lif'이고 a[0:4]는 'Life'가 되는 것이다. 이 부분이 문자열 연산에서 가장 혼동하기 쉬운 부분이니 장 마지막의 연습 문제를 많이 풀어 보면서 몸에 익히기 바란다.

문자열을 슬라이싱하는 방법

슬라이싱의 예를 조금 더 보자.

>>> a[0:5]
'Life '

위 예는 a[0] + a[1] + a[2] + a[3] + a[4]와 동일하다. a[4]는 공백 문자이기 때문에 'Life'가 아닌 'Life '가 출력된다. 공백 문자 역시 L, i, f, e 같은 문자와 동일하게 취급되는 것을 잊지 말자. 'Life'와 'Life '는 완전히 다른 문자열이다.

슬라이싱할 때 항상 시작 번호가 0일 필요는 없다.

>>> a[0:2]
'Li'
>>> a[5:7]
'is'
>>> a[12:17]
'short'

a[시작 번호:끝 번호]에서 끝 번호 부분을 생략하면 시작 번호부터 그 문자열의 끝까지 뽑아낸다.

>>> a[19:]
'You need Python'

a[시작 번호:끝 번호]에서 시작 번호를 생략하면 문자열의 처음부터 끝 번호까지 뽑아낸다.

>>> a[:17]
'Life is too short'

a[시작 번호:끝 번호]에서 시작 번호와 끝 번호를 생략하면 문자열의 처음부터 끝까지를 뽑아낸다.

>>> a[:]
'Life is too short, You need Python'

슬라이싱에서도 인덱싱과 마찬가지로 마이너스(-) 기호를 사용할 수 있다.

>>> a[19:-7]
'You need'

위 소스 코드에서 a[19:-7]이 뜻하는 것은 a[19]에서부터 a[-8]까지를 말한다. 이 역시 a[-7]은 포함하지 않는다.

 

 

 

문자열 슬라이싱이란?

그렇다면 "Life is too short, You need Python" 문자열에서 단순히 한 문자만을 뽑아내는 것이 아니라 'Life' 또는 'You' 같은 단어를 뽑아내는 방법은 없을까?

다음과 같이 하면 된다.

>>> a = "Life is too short, You need Python"
>>> b = a[0] + a[1] + a[2] + a[3]
>>> b
'Life'

위 방법처럼 단순하게 접근할 수도 있지만 파이썬에서는 더 좋은 방법을 제공한다. 바로 슬라이싱(Slicing) 기법이다.

인덱싱 기법과 슬라이싱 기법은 뒤에서 배울 자료형인 리스트나 튜플에서도 사용할 수 있다.

위 예는 슬라이싱 기법으로 다음과 같이 간단하게 처리할 수 있다.

>>> a = "Life is too short, You need Python"
>>> a[0:4]
'Life'

a[0:4]가 뜻하는 것은 a 문자열, 즉 "Life is too short, You need Python" 문장에서 자리 번호 0부터 4까지의 문자를 뽑아낸다는 뜻이다. 하지만 다음과 같은 의문이 생길 것이다. a[0]은 L, a[1]은 i, a[2]는 f, a[3]은 e니까 a[0:3]으로도 Life라는 단어를 뽑아낼 수 있지 않을까? 다음 예로 확인해 보자.

>>> a[0:3]
'Lif'

이렇게 되는 이유는 간단하다. 슬라이싱 기법으로 a[시작 번호:끝 번호]를 지정할 때 끝 번호에 해당하는 것은 포함하지 않기 때문이다. a[0:3]을 수식으로 나타내면 다음과 같다.

0 <= a < 3

이 수식을 만족하는 것은 a[0], a[1], a[2]이다. 따라서 a[0:3]은 'Lif'이고 a[0:4]는 'Life'가 되는 것이다. 이 부분이 문자열 연산에서 가장 혼동하기 쉬운 부분이니 장 마지막의 연습 문제를 많이 풀어 보면서 몸에 익히기 바란다.

문자열을 슬라이싱하는 방법

슬라이싱의 예를 조금 더 보자.

>>> a[0:5]
'Life '

위 예는 a[0] + a[1] + a[2] + a[3] + a[4]와 동일하다. a[4]는 공백 문자이기 때문에 'Life'가 아닌 'Life '가 출력된다. 공백 문자 역시 L, i, f, e 같은 문자와 동일하게 취급되는 것을 잊지 말자. 'Life'와 'Life '는 완전히 다른 문자열이다.

슬라이싱할 때 항상 시작 번호가 0일 필요는 없다.

>>> a[0:2]
'Li'
>>> a[5:7]
'is'
>>> a[12:17]
'short'

a[시작 번호:끝 번호]에서 끝 번호 부분을 생략하면 시작 번호부터 그 문자열의 끝까지 뽑아낸다.

>>> a[19:]
'You need Python'

a[시작 번호:끝 번호]에서 시작 번호를 생략하면 문자열의 처음부터 끝 번호까지 뽑아낸다.

>>> a[:17]
'Life is too short'

a[시작 번호:끝 번호]에서 시작 번호와 끝 번호를 생략하면 문자열의 처음부터 끝까지를 뽑아낸다.

>>> a[:]
'Life is too short, You need Python'

슬라이싱에서도 인덱싱과 마찬가지로 마이너스(-) 기호를 사용할 수 있다.

>>> a[19:-7]
'You need'

위 소스 코드에서 a[19:-7]이 뜻하는 것은 a[19]에서부터 a[-8]까지를 말한다. 이 역시 a[-7]은 포함하지 않는다.

 

 

 

 

 

 

 

리스트는 어떻게 만들고 사용할까?

리스트를 사용하면 1, 3, 5, 7, 9 숫자 모음을 다음과 같이 간단하게 표현할 수 있다.

>>> odd = [1, 3, 5, 7, 9]

리스트를 만들 때는 위에서 보는 것과 같이 대괄호([ ])로 감싸 주고 각 요솟값은 쉼표(,)로 구분해 준다.

리스트명 = [요소1, 요소2, 요소3, ...]

여러 가지 리스트의 생김새를 살펴보면 다음과 같다.

>>> a = []
>>> b = [1, 2, 3]
>>> c = ['Life', 'is', 'too', 'short']
>>> d = [1, 2, 'Life', 'is']
>>> e = [1, 2, ['Life', 'is']]

리스트는 a처럼 아무것도 포함하지 않아 비어 있는 리스트([ ])일 수도 있고 b처럼 숫자를 요솟값으로 가질 수도 있고 c처럼 문자열을 요솟값으로 가질 수도 있다. 또한 d처럼 숫자와 문자열을 함께 요솟값으로 가질 수도 있으며 e처럼 리스트 자체를 요솟값으로 가질 수도 있다. 즉 리스트 안에는 어떠한 자료형도 포함시킬 수 있다.

비어 있는 리스트는 a = list()로 생성할 수도 있다.

리스트의 인덱싱과 슬라이싱

리스트도 문자열처럼 인덱싱과 슬라이싱이 가능하다. 백문이 불여일견. 말로 설명하는 것보다 직접 예를 실행해 보면서 리스트의 기본 구조를 이해하는 것이 쉽다. 대화형 인터프리터로 따라 하며 확실하게 이해하자.

리스트의 인덱싱

리스트 역시 문자열처럼 인덱싱을 적용할 수 있다. 먼저 a 변수에 [1, 2, 3] 값을 설정한다.

>>> a = [1, 2, 3]
>>> a
[1, 2, 3]

a[0]은 리스트 a의 첫 번째 요솟값을 말한다.

>>> a[0]
1

다음 예는 리스트의 첫 번째 요소인 a[0]과 세 번째 요소인 a[2]의 값을 더한 것이다.

>>> a[0] + a[2]
4

이것은 1 + 3으로 해석되어 값 4를 출력한다.

문자열을 공부할 때 이미 살펴보았지만 파이썬은 숫자를 0부터 세기 때문에 a[1]이 리스트 a의 첫 번째 요소가 아니라 a[0]이 리스트 a의 첫 번째 요소임을 명심하자. a[-1]은 문자열에서와 마찬가지로 리스트 a의 마지막 요솟값을 말한다.

>>> a[-1]
3

이번에는 다음 예처럼 리스트 a를 숫자 1, 2, 3과 또 다른 리스트인 ['a', 'b', 'c']를 포함하도록 만들어 보자.

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

그리고 다음 예를 따라 해 보자.

>>> a[0]
1
>>> a[-1]
['a', 'b', 'c']
>>> a[3]
['a', 'b', 'c']

예상한 대로 a[-1]은 마지막 요솟값 ['a', 'b', 'c']를 나타낸다. a[3]은 리스트 a의 네 번째 요소를 나타내기 때문에 마지막 요소를 나타내는 a[-1]과 동일한 결괏값을 보여 준다.

그렇다면 여기에서 리스트 a에 포함된 ['a', 'b', 'c'] 리스트에서 'a' 값을 인덱싱을 사용해 끄집어낼 수 있는 방법은 없을까? 다음 예를 보자.

>>> a[-1][0]
'a'

위와 같이 하면 'a'를 끄집어낼 수 있다. a[-1]이 ['a', 'b', 'c'] 리스트라는 것은 이미 말했다. 바로 이 리스트에서 첫 번째 요소를 불러오기 위해 [0]을 붙여 준 것이다.

다음 예도 마찬가지 경우이므로 어렵지 않게 이해될 것이다.

>>> a[-1][1]
'b'
>>> a[-1][2]
'c'

점프 투 파이썬[삼중 리스트에서 인덱싱하기]

조금 복잡하지만 다음과 같은 것도 가능하다.

>>> a = [1, 2, ['a', 'b', ['Life', 'is']]]

리스트 a 안에 ['a', 'b', ['Life', 'is']] 리스트가 포함되어 있고, 그 리스트 안에 다시 ['Life', 'is'] 리스트가 포함되어 있다. 삼중 구조의 리스트이다.

이 경우 'Life' 문자열만 끄집어내려면 다음과 같이 해야 한다.

>>> a[2][2][0]
'Life'

위 예는 리스트 a의 세 번째 요소인 리스트 ['a', 'b', ['Life', 'is']]에서 세 번째 요소인 리스트 ['Life', 'is']의 첫 번째 요소를 나타낸다.

리스트를 다중으로 중첩해서 사용하는 것은 혼란스럽기 때문에 자주 사용하지는 않는다.

리스트의 슬라이싱

문자열과 마찬가지로 리스트에서도 슬라이싱 기법을 적용할 수 있다. 슬라이싱은 "나눈다"는 뜻이라고 했다.

자, 그럼 리스트의 슬라이싱에 대해서 살펴보자.

>>> a = [1, 2, 3, 4, 5]
>>> a[0:2]
[1, 2]

앞의 예를 문자열에서 슬라이싱했던 것과 비교해 보자.

>>> a = "12345"
>>> a[0:2]
'12'

2가지가 완전히 동일하게 사용되었음을 눈치챘을 것이다. 문자열에서 했던 것과 사용법이 완전히 동일하다.

몇 가지 예를 더 들어 보자.

>>> a = [1, 2, 3, 4, 5]
>>> b = a[:2]
>>> c = a[2:]
>>> b
[1, 2]
>>> c
[3, 4, 5]

b 변수는 리스트 a의 첫 번째 요소부터 두 번째 요소인 a[1]까지 나타내는 리스트이다. 물론 a[2] 값인 3은 포함되지 않는다. c라는 변수는 리스트 a의 세 번째 요소부터 끝까지 나타내는 리스트이다.

점프 투 파이썬[중첩된 리스트에서 슬라이싱하기]

리스트가 포함된 중첩 리스트 역시 슬라이싱 방법은 똑같이 적용된다.

>>> a = [1, 2, 3, ['a', 'b', 'c'], 4, 5]
>>> a[2:5]
[3, ['a', 'b', 'c'], 4]
>>> a[3][:2]
['a', 'b']

위 예에서 a[3]은 ['a', 'b', 'c']를 나타낸다. 따라서 a[3][:2]는 ['a', 'b', 'c']의 첫 번째 요소부터 세 번째 요소 직전까지의 값, 즉 ['a', 'b']를 나타내는 리스트가 된다.

리스트 연산하기

리스트 역시 + 기호를 사용해서 더할 수 있고 * 기호를 사용해서 반복할 수 있다. 문자열과 마찬가지로 리스트에서도 되는지 직접 확인해 보자.

리스트 더하기(+)

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a + b
[1, 2, 3, 4, 5, 6]

리스트 사이에서 + 기호는 2개의 리스트를 합치는 기능을 한다. 문자열에서 "abc" + "def" = "abcdef"가 되는 것과 같은 이치이다.

리스트 반복하기(*)

>>> a = [1, 2, 3]
>>> a * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

위에서 볼 수 있듯이 [1, 2, 3] 리스트가 세 번 반복되어 새로운 리스트를 만들어낸다. 문자열에서 "abc" * 3 = "abcabcabc" 가 되는 것과 같은 이치이다.

리스트 길이구하기

리스트 길이를 구하기 위해서는 다음처럼 len 함수를 사용해야 한다.

>>> a = [1, 2, 3]
>>> len(a)
3

len 함수는 문자열, 리스트 외에 앞으로 배울 튜플과 딕셔너리에도 사용할 수 있는 함수이다. 실습에서 자주 사용하니 잘 기억해 두자.

점프 투 파이썬[초보자가 범하기 쉬운 리스트 연산 오류]

다음 소스 코드를 입력했을 때 결괏값은 어떻게 나올까?

>>> a = [1, 2, 3]
>>> a[2] + "hi"

a[2]의 값인 3과 문자열 hi가 더해져서 3hi가 출력될 것이라고 생각할 수 있다. 하지만 다음 결과를 보자. 형 오류(TypeError)가 발생했다. 오류의 원인은 무엇일까?

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

a[2]에 저장된 값은 3이라는 정수인데 "hi"는 문자열이다. 정수와 문자열은 당연히 서로 더할 수 없기 때문에 형 오류가 발생한 것이다.

만약 숫자와 문자열을 더해서 '3hi'처럼 만들고 싶다면 다음처럼 숫자 3을 문자 '3'으로 바꾸어야 한다.

>>> str(a[2]) + "hi"
3hi

str 함수는 정수나 실수를 문자열로 바꾸어 주는 파이썬의 내장 함수이다.

리스트의 수정과 삭제

리스트는 값을 수정하거나 삭제할 수 있다.

 

리스트에서 값 수정하기

>>> a = [1, 2, 3]
>>> a[2] = 4
>>> a
[1, 2, 4]

a[2]의 요솟값 3이 4로 바뀌었다.

del 함수 사용해 리스트 요소 삭제하기

>>> a = [1, 2, 3]
>>> del a[1]
>>> a
[1, 3]

del a[x]는 x번째 요솟값을 삭제한다. 여기에서는 a 리스트에서 a[1]을 삭제하는 방법을 보여준다. del 함수는 파이썬이 자체적으로 가지고 있는 삭제 함수이며 다음과 같이 사용한다.

del 객체

객체란 파이썬에서 사용되는 모든 자료형을 말한다.

다음처럼 슬라이싱 기법을 사용하여 리스트의 요소 여러 개를 한꺼번에 삭제할 수도 있다.

>>> a = [1, 2, 3, 4, 5]
>>> del a[2:]
>>> a
[1, 2]

a[2:]에 해당하는 리스트의 요소들이 삭제되었다.

리스트의 요소를 삭제하는 방법에는 2가지가 더 있다. 그것은 리스트의 remove와 pop 함수를 사용하는 방법인데 이것에 대해서는 바로 이어지는 리스트 관련 함수에서 설명한다.

리스트 관련 함수들

문자열과 마찬가지로 리스트 변수 이름 뒤에 '.'를 붙여서 여러 가지 리스트 관련 함수를 사용할 수 있다. 유용하게 사용되는 리스트 관련 함수 몇 가지에 대해서만 알아보기로 하자.

리스트에 요소 추가(append)

append를 사전에서 검색해 보면 "덧붙이다, 첨부하다"라는 뜻이 있다. 이 뜻을 안다면 다음 예가 바로 이해될 것이다. append(x)는 리스트의 맨 마지막에 x를 추가하는 함수이다.

>>> a = [1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]

리스트 안에는 어떤 자료형도 추가할 수 있다. 다음 예는 리스트에 다시 리스트를 추가한 결과이다.

>>> a.append([5,6])
>>> a
[1, 2, 3, 4, [5, 6]]

리스트 정렬(sort)

sort 함수는 리스트의 요소를 순서대로 정렬해 준다.

>>> a = [1, 4, 3, 2]
>>> a.sort()
>>> a
[1, 2, 3, 4]

문자 역시 알파벳 순서로 정렬할 수 있다.

>>> a = ['a', 'c', 'b']
>>> a.sort()
>>> a
['a', 'b', 'c']

리스트 뒤집기(reverse)

reverse 함수는 리스트를 역순으로 뒤집어 준다. 이때 리스트 요소들을 순서대로 정렬한 다음 다시 역순으로 정렬하는 것이 아니라 그저 현재의 리스트를 그대로 거꾸로 뒤집는다.

>>> a = ['a', 'c', 'b']
>>> a.reverse()
>>> a
['b', 'c', 'a']

인덱스 반환(index)

index(x) 함수는 리스트에 x 값이 있으면 x의 인덱스 값을 리턴한다.

>>> a = [1,2,3]
>>> a.index(3)
2
>>> a.index(1)
0

위 예에서 리스트 a에 있는 숫자 3의 위치는 a[2]이므로 2를 리턴하고, 숫자 1의 위치는 a[0]이므로 0을 리턴한다.

다음 예에서 값 0은 a 리스트에 존재하지 않기 때문에 값 오류(ValueError)가 발생한다.

>>> a.index(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 0 is not in list

리스트에 요소 삽입(insert)

insert(a, b)는 리스트의 a번째 위치에 b를 삽입하는 함수이다. 파이썬은 숫자를 0부터 센다는 것을 반드시 기억하자.

>>> a = [1, 2, 3]
>>> a.insert(0, 4)
>>> a
[4, 1, 2, 3]

위 예는 0번째 자리, 즉 첫 번째 요소(a[0]) 위치에 값 4를 삽입하라는 뜻이다.

>>> a.insert(3, 5)
>>> a
[4, 1, 2, 5, 3]

위 예는 리스트 a의 a[3], 즉 네 번째 요소 위치에 값 5를 삽입하라는 뜻이다.

리스트 요소 제거(remove)

remove(x)는 리스트에서 첫 번째로 나오는 x를 삭제하는 함수이다.

>>> a = [1, 2, 3, 1, 2, 3]
>>> a.remove(3)
>>> a
[1, 2, 1, 2, 3]

a가 3이라는 값을 2개 가지고 있을 경우 첫 번째 3만 제거되는 것을 알 수 있다.

>>> a.remove(3)
>>> a
[1, 2, 1, 2]

remove(3)을 한 번 더 실행하면 다시 3이 삭제된다.

리스트 요소 끄집어내기(pop)

pop()은 리스트의 맨 마지막 요소를 리턴하고 그 요소는 삭제한다.

>>> a = [1,2,3]
>>> a.pop()
3
>>> a
[1, 2]

a 리스트 [1, 2, 3]에서 3을 끄집어내고 최종적으로 [1, 2]만 남는 것을 볼 수 있다.

pop(x)는 리스트의 x번째 요소를 리턴하고 그 요소는 삭제한다.

>>> a = [1,2,3]
>>> a.pop(1)
2
>>> a
[1, 3]

a.pop(1)은 a[1]의 값을 끄집어내어 리턴한다. 다시 a를 출력해 보면 끄집어낸 값이 삭제된 것을 확인할 수 있다.

리스트에 포함된 요소 x의 개수 세기(count)

count(x)는 리스트 안에 x가 몇 개 있는지 조사하여 그 개수를 리턴하는 함수이다.

>>> a = [1,2,3,1]
>>> a.count(1)
2

1이라는 값이 리스트 a에 2개 들어 있으므로 2를 리턴한다.

리스트 확장(extend)

extend(x)에서 x에는 리스트만 올 수 있으며 원래의 a 리스트에 x 리스트를 더하게 된다.

>>> a = [1,2,3]
>>> a.extend([4,5])
>>> a
[1, 2, 3, 4, 5]
>>> b = [6, 7]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6, 7]

a.extend([4, 5])는 a += [4, 5]와 동일하다.

a += [4, 5]는 a = a + [4, 5]와 동일한 표현식이다.

 

 

 

 

 

 

 

 

 

 

 

딕셔너리란?

사람은 누구든지 "이름" = "홍길동", "생일" = "몇 월 며칠" 등으로 나타낼 수 있다. 파이썬은 영리하게도 이러한 대응 관계를 나타낼 수 있는 자료형을 가지고 있다. 요즘 사용하는 대부분의 언어도 이러한 대응 관계를 나타내는 자료형을 갖고 있는데, 이를 연관 배열(Associative array) 또는 해시(Hash)라고 한다.

파이썬에서는 이러한 자료형을 딕셔너리(Dictionary)라고 하는데, 단어 그대로 해석하면 사전이라는 뜻이다. 즉 "people"이라는 단어에 "사람", "baseball"이라는 단어에 "야구"라는 뜻이 부합되듯이 딕셔너리는 Key와 Value를 한 쌍으로 갖는 자료형이다. 예컨대 Key가 "baseball"이라면 Value는 "야구"가 될 것이다.

딕셔너리는 리스트나 튜플처럼 순차적으로(sequential) 해당 요솟값을 구하지 않고 Key를 통해 Value를 얻는다. 이것이 바로 딕셔너리의 가장 큰 특징이다. baseball이라는 단어의 뜻을 찾기 위해 사전의 내용을 순차적으로 모두 검색하는 것이 아니라 baseball이라는 단어가 있는 곳만 펼쳐 보는 것이다.

딕셔너리는 어떻게 만들까?

다음은 기본 딕셔너리의 모습이다.

{Key1:Value1, Key2:Value2, Key3:Value3, ...}

Key와 Value의 쌍 여러 개가 { }로 둘러싸여 있다. 각각의 요소는 Key : Value 형태로 이루어져 있고 쉼표(,)로 구분되어 있다.

다음 딕셔너리 예를 살펴보자.

>>> dic = {'name':'pey', 'phone':'010-9999-1234', 'birth': '1118'}

위에서 Key는 각각 'name', 'phone', 'birth'이고, 각각의 Key에 해당하는 Value는 'pey', '010-9999-1234', '1118'이 된다.

딕셔너리 dic의 정보

keyvalue

name pey
phone 010-9999-1234
birth 1118

다음 예는 Key로 정수 값 1, Value로 문자열 'hi'를 사용한 예이다.

>>> a = {1: 'hi'}

또한 다음 예처럼 Value에 리스트도 넣을 수 있다.

>>> a = { 'a': [1,2,3]}

딕셔너리 쌍 추가, 삭제하기

딕셔너리 쌍을 추가하는 방법과 삭제하는 방법을 살펴보자. 먼저 딕셔너리에 쌍을 추가해 보자.

딕셔너리 쌍 추가하기

>>> a = {1: 'a'}
>>> a[2] = 'b'
>>> a
{1: 'a', 2: 'b'}

{1: 'a'} 딕셔너리에 a[2] = 'b'와 같이 입력하면 딕셔너리 a에 Key와 Value가 각각 2와 'b'인 {2 : 'b'} 딕셔너리 쌍이 추가된다.

>>> a['name'] = 'pey'
>>> a
{1: 'a', 2: 'b', 'name': 'pey'}

딕셔너리 a에 {'name': 'pey'} 쌍이 추가되었다.

>>> a[3] = [1,2,3]
>>> a
{1: 'a', 2: 'b', 'name': 'pey', 3: [1, 2, 3]}

Key는 3, Value는 [1, 2, 3]을 가지는 한 쌍이 또 추가되었다.

딕셔너리 요소 삭제하기

>>> del a[1]
>>> a
{2: 'b', 'name': 'pey', 3: [1, 2, 3]}

위 예제는 딕셔너리 요소를 지우는 방법을 보여 준다. del 함수를 사용해서 del a[key]처럼 입력하면 지정한 Key에 해당하는 {key : value} 쌍이 삭제된다.

딕셔너리를 사용하는 방법

"딕셔너리는 주로 어떤 것을 표현하는 데 사용할까?"라는 의문이 들 것이다. 예를 들어 4명의 사람이 있다고 가정하고, 각자의 특기를 표현할 수 있는 좋은 방법에 대해서 생각해 보자. 리스트나 문자열로는 표현하기가 상당히 까다로울 것이다. 하지만 파이썬의 딕셔너리를 사용한다면 이 상황을 표현하기가 정말 쉽다.

다음 예를 보자.

{"김연아":"피겨스케이팅", "류현진":"야구", "손흥민":"축구", "귀도":"파이썬"}

사람 이름과 특기를 한 쌍으로 하는 딕셔너리이다. 정말 간편하지 않은가?

지금껏 우리는 딕셔너리를 만드는 방법에 대해서만 살펴보았는데 딕셔너리를 제대로 활용하기 위해서는 알아야 할 것이 더 있다. 이제부터 하나씩 알아보자.

딕셔너리에서 Key 사용해 Value 얻기

다음을 따라해 보자.

>>> grade = {'pey': 10, 'julliet': 99}
>>> grade['pey']
10
>>> grade['julliet']
99

리스트나 튜플, 문자열은 요솟값을 얻고자 할 때 인덱싱이나 슬라이싱 기법 중 하나를 사용했다. 하지만 딕셔너리는 단 한 가지 방법뿐이다. 바로 Key를 사용해서 Value를 구하는 방법이다. 위 예에서 'pey'라는 Key의 Value를 얻기 위해 grade['pey']를 사용한 것처럼 어떤 Key의 Value를 얻기 위해서는 딕셔너리변수이름[Key]를 사용한다.

몇 가지 예를 더 보자.

>>> a = {1:'a', 2:'b'}
>>> a[1]
'a'
>>> a[2]
'b'

먼저 a 변수에 {1:'a', 2:'b'} 딕셔너리를 대입하였다. 위 예에서 볼 수 있듯이 a[1]은 'a' 값을 리턴한다. 여기에서 a[1]이 의미하는 것은 리스트나 튜플의 a[1]과는 전혀 다르다. 딕셔너리 변수에서 [ ] 안의 숫자 1은 두 번째 요소를 뜻하는 것이 아니라 Key에 해당하는 1을 나타낸다. 앞에서도 말했듯이 딕셔너리는 리스트나 튜플에 있는 인덱싱 방법을 적용할 수 없다. 따라서 a[1]은 딕셔너리 {1:'a', 2:'b'}에서 Key가 1인 것의 Value인 'a'를 리턴한다. a[2] 역시 마찬가지이다.

이번에는 a라는 변수에 앞의 예에서 사용한 딕셔너리의 Key와 Value를 뒤집어 놓은 딕셔너리를 대입해 보자.

>>> a = {'a':1, 'b':2}
>>> a['a']
1
>>> a['b']
2

역시 a['a'], a['b']처럼 Key를 사용해서 Value를 얻을 수 있다. 정리하면, 딕셔너리 a는 a[Key]로 Key에 해당하는 Value를 얻는다.

다음 예는 이전에 한 번 언급한 딕셔너리인데 Key를 사용해서 Value를 얻는 방법을 잘 보여 준다.

>>> dic = {'name':'pey', 'phone':'010-9999-1234', 'birth': '1118'}
>>> dic['name']
'pey'
>>> dic['phone']
'010-9999-1234'
>>> dic['birth']
'1118'

딕셔너리 만들 때 주의할 사항

딕셔너리에서 Key는 고유한 값이므로 중복되는 Key 값을 설정해 놓으면 하나를 제외한 나머지 것들이 모두 무시된다는 점을 주의해야 한다. 다음 예에서 볼 수 있듯이 동일한 Key가 2개 존재할 경우 1:'a' 쌍이 무시된다.

>>> a = {1:'a', 1:'b'}
>>> a
{1: 'b'}

이렇게 Key가 중복되었을 때 1개를 제외한 나머지 Key:Value 값이 모두 무시되는 이유는 Key를 통해서 Value를 얻는 딕셔너리의 특징에서 비롯된다. 즉 동일한 Key가 존재하면 어떤 Key에 해당하는 Value를 불러야 할지 알 수 없기 때문이다.

또 한 가지 주의해야 할 사항은 Key에 리스트는 쓸 수 없다는 것이다. 하지만 튜플은 Key로 쓸 수 있다. 딕셔너리의 Key로 쓸 수 있느냐 없느냐는 Key가 변하는(mutable) 값인지 변하지 않는(immutable) 값인지에 달려 있다. 리스트는 그 값이 변할 수 있기 때문에 Key로 쓸 수 없다. 다음 예처럼 리스트를 Key로 설정하면 리스트를 키 값으로 사용할 수 없다는 오류가 발생한다.

>>> a = {[1,2] : 'hi'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

단, Value에는 변하는 값이든 변하지 않는 값이든 상관없이 아무 값이나 넣을 수 있다.

딕셔너리 관련 함수들

딕셔너리를 자유자재로 사용하기 위해 딕셔너리가 자체적으로 가지고 있는 관련 함수를 사용해 보자.

Key 리스트 만들기(keys)

>>> a = {'name': 'pey', 'phone': '010-9999-1234', 'birth': '1118'}
>>> a.keys()
dict_keys(['name', 'phone', 'birth'])

a.keys()는 딕셔너리 a의 Key만을 모아서 dict_keys 객체를 리턴한다.

 

>>> for k in a.keys():
...    print(k)
...
name
phone
birth

print(k)를 입력할 때 들여쓰기를 하지 않으면 오류가 발생하니 주의하자. for문 등 반복 구문에 대해서는 03장에서 자세히 살펴본다.

dict_keys 객체를 리스트로 변환하려면 다음과 같이 하면 된다.

>>> list(a.keys())
['name', 'phone', 'birth']

Value 리스트 만들기(values)

>>> a.values()
dict_values(['pey', '010-9999-1234', '1118'])

Key를 얻는 것과 마찬가지 방법으로 Value만 얻고 싶다면 values 함수를 사용하면 된다. values 함수를 호출하면 dict_values 객체를 돌려준다.

Key, Value 쌍 얻기(items)

>>> a.items()
dict_items([('name', 'pey'), ('phone', '010-9999-1234'), ('birth', '1118')])

items 함수는 Key와 Value의 쌍을 튜플로 묶은 값을 dict_items 객체로 돌려준다.

Key: Value 쌍 모두 지우기(clear)

>>> a.clear()
>>> a
{}

clear 함수는 딕셔너리 안의 모든 요소를 삭제한다.

빈 리스트를 [], 빈 튜플을 ()로 표현하는 것과 마찬가지로 빈 딕셔너리도 {}로 표현한다.

Key로 Value얻기(get)

>>> a = {'name':'pey', 'phone':'010-9999-1234', 'birth': '1118'}
>>> a.get('name')
'pey'
>>> a.get('phone')
'010-9999-1234'

get(x) 함수는 x라는 Key에 대응되는 Value를 리턴한다. 앞에서 살펴보았듯이 a.get('name')은 a['name']을 사용했을 때와 동일한 결괏값을 리턴한다.

다만 다음 예제에서 볼 수 있듯이 a['nokey']처럼 딕셔너리에 존재하지 않는 키로 값을 가져오려고 할 경우 a['nokey'] 방식은 오류를 발생시키고 a.get('nokey') 방식은 None을 리턴한다는 차이가 있다. 어떤것을 사용할지는 여러분의 선택이다.

여기에서 None은 "거짓"이라는 뜻이라고만 알아두자.

>>> a = {'name':'pey', 'phone':'010-9999-1234', 'birth': '1118'}
>>> print(a.get('nokey'))
None
>>> print(a['nokey'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'nokey'

딕셔너리 안에 찾으려는 Key가 없을 경우 미리 정해 둔 디폴트 값을 대신 가져오게 하고 싶을 때에는 get(x, '디폴트 값')을 사용하면 편리하다.

>>> a.get('nokey', 'foo')
'foo'

딕셔너리 a에는 'nokey'에 해당하는 Key가 없다. 따라서 디폴트 값인 'foo'를 리턴한다.

해당 Key가 딕셔너리 안에 있는지 조사하기(in)

>>> a = {'name':'pey', 'phone':'010-9999-1234', 'birth': '1118'}
>>> 'name' in a
True
>>> 'email' in a
False

'name' 문자열은 a 딕셔너리의 Key 중 하나이다. 따라서 'name' in a를 호출하면 참(True)을 리턴한다. 반대로 'email'은 a 딕셔너리 안에 존재하지 않는 Key이므로 거짓(False)을 리턴한다.

 

 

 

 

 

 

 

 

 

튜플 (tuple)

  • ()로 둘러쌓여 있음.
  • 리스트는 값의 생성, 삭제,수정이 가능하지만 튜플은 그 값을 바꿀 수 없음.
#튜플 
- t1 = ()
- t2 = (1,2,3,4)
- t3 = ("a","b",("ab","cd"))
- t4 =. 1,2,3  # --> 괄호를 생략함.
- t5 = (1,) # --> 1개의 요소를 가질때는 반드시 콤마(,)를 붙여야함.

- 튜플과 리스트의 차이점?

  • 가장 큰 차이점은 리스트의 항목 값은 변화가 가능하고, 튜플의 항목 값은 변화가 불가능하다. 따라서 프로그램이 실행되는 동안 그 값이 항상 변하지 않길 바란다면 튜플을 사용해야한다.
  • 튜플을 삭제하거나 요소값을 변경하려 한다면 오류가 발생한다.
  • 값을 변화시킬수 없다는 점만 제외한다면 리스트와 완전히 동일함.
    • 인덱싱 : ex) t1[0],t1[1]
    • 슬라이싱 ex) t1[1:4]
    • 더하기 : +
    • 곱하기 : *
    • 길이 구하기 : len()



📌 집합 (set)

  • 집합 자료형은 set 키워드를 사용해 만들 수 있음.
  • 중복을 허용하지 않음.
  • 순서(ordered)가 없으므로 인덱싱으로 값을 얻을 수 없음.
    따라서 set 자료형에 저장된 값을 인덱싱으로 접근하려면 리스트와 튜플로 변환한 후 접근해야함.
s1 = set([1,3,4,5,5])
print(s1)

# 중복 허용 X
-> {1, 3, 4, 5}

s2 = set("hello")
s3 = set("happy")

print(s2)
print(s3)

# 중복을 허용하지 않으며, 순서가 없기 때문에
-> {'l', 'e', 'o', 'h'}
-> {'a', 'y', 'p', 'h'}

- 교집합, 합집합, 차집합 구하기

  • set 자료형을 유용하게 사용할 수 있는 경우는 교집합, 합집합,차집합을 구할때이다.

1. 교집합 구하기

--> &를 사용하거나 또는, .intersection() 사용하기

>>> s1 = set([1, 2, 3, 4, 5, 6])
>>> s2 = set([4, 5, 6, 7, 8, 9])

#첫번째 방법
>>> s1 & s2
{4, 5, 6}

#두번째 방법
>>> s1.intersection(s2)
{4, 5, 6}

2. 합집합 구하기

  • 중복 값은 한개씩만 포함된다.

--> | 사용하거나 또는, .union() 사용하기.

>>> s1 = set([1, 2, 3, 4, 5, 6])
>>> s2 = set([4, 5, 6, 7, 8, 9])

#첫번째 방법
>>> s1 | s2
{1, 2, 3, 4, 5, 6, 7, 8, 9}

#두번째 방법
>>> s1.union(s2)
{1, 2, 3, 4, 5, 6, 7, 8, 9}

3. 차집합 구하기

--> -(빼기)를 사용하거나 또는, .difference()사용하기

>>> s1 = set([1, 2, 3, 4, 5, 6])
>>> s2 = set([4, 5, 6, 7, 8, 9])

#첫번재 방법
>>> s1 - s2
{1, 2, 3}
>>> s2 - s1
{8, 9, 7}

#두번째 방법
>>> s1.difference(s2)
{1, 2, 3}
>>> s2.difference(s1)
{8, 9, 7}

📌 집합 관련 함수들

  • .add() : 값 1개 추가하기
  • .update : 값 여러 개 추가하기
  • .remove() : 특정 값 제거하기






while과 for 반복문

개발을 하다 보면 여러 동작을 반복해야 하는 경우가 종종 생깁니다.

상품 목록에서 상품을 차례대로 출력하거나 숫자를 1부터 10까지 하나씩 증가시키면서 동일한 코드를 반복 실행해야 하는 경우같이 말이죠.

반복문(loop) 을 사용하면 동일한 코드를 여러 번 반복할 수 있습니다.

‘while’ 반복문

while 반복문의 문법은 다음과 같습니다.

while (condition) {
  // 코드
  // '반복문 본문(body)'이라 불림
}

condition(조건)이 truthy 이면 반복문 본문의 코드가 실행됩니다.

아래 반복문은 조건 i < 3을 만족할 동안 i를 출력해줍니다.

 
 
let i = 0;
while (i < 3) { // 0, 1, 2가 출력됩니다.
  alert( i );
  i++;
}

반복문 본문이 한 번 실행되는 것을 반복(iteration, 이터레이션) 이라고 부릅니다. 위 예시에선 반복문이 세 번의 이터레이션을 만듭니다.

i++가 없었다면 이론적으로 반복문이 영원히 반복되었을 겁니다. 그런데 브라우저는 이런 무한 반복을 멈추게 해주는 실질적인 수단을 제공합니다. 서버 사이드 자바스크립트도 이런 수단을 제공해 주므로 무한으로 반복되는 프로세스를 종료할 수 있습니다.

반복문 조건엔 비교뿐만 아니라 모든 종류의 표현식, 변수가 올 수 있습니다. 조건은 while에 의해 평가되고, 평가 후엔 불린값으로 변경됩니다.

아래 예시에선 while (i != 0)을 짧게 줄여 while (i)로 만들어보았습니다.

 
 
let i = 3;
while (i) { // i가 0이 되면 조건이 falsy가 되므로 반복문이 멈춥니다.
  alert( i );
  i--;
}
본문이 한 줄이면 대괄호를 쓰지 않아도 됩니다.

반복문 본문이 한 줄짜리 문이라면 대괄호 {…}를 생략할 수 있습니다.

 
 
let i = 3;
while (i) alert(i--);

‘do…while’ 반복문

do..while 문법을 사용하면 condition을 반복문 본문 아래로 옮길 수 있습니다.

do {
  // 반복문 본문
} while (condition);

이때 본문이 먼저 실행되고, 조건을 확인한 후 조건이 truthy인 동안엔 본문이 계속 실행됩니다.

예시:

 
 
let i = 0;
do {
  alert( i );
  i++;
} while (i < 3);

do..while 문법은 조건이 truthy 인지 아닌지에 상관없이, 본문을 최소한 한 번이라도 실행하고 싶을 때만 사용해야 합니다. 대다수의 상황에선 do..while보다 while(…) {…}이 적합합니다.

‘for’ 반복문

for 반복문은 while 반복문보다는 복잡하지만 가장 많이 쓰이는 반복문입니다.

문법은 다음과 같습니다.

for (begin; condition; step) {
  // ... 반복문 본문 ...
}

for문을 구성하는 각 요소가 무엇을 의미하는지 알아봅시다. 아래 반복문을 실행하면 i가 0부터 3이 될 때까지(단, 3은 포함하지 않음) alert(i)가 호출됩니다.

 
 
for (let i = 0; i < 3; i++) { // 0, 1, 2가 출력됩니다.
  alert(i);
}

이제 for문의 구성 요소를 하나씩 살펴봅시다.

구성 요소
begin i = 0 반복문에 진입할 때 단 한 번 실행됩니다.
condition i < 3 반복마다 해당 조건이 확인됩니다. false이면 반복문을 멈춥니다.
body alert(i) condition이 truthy일 동안 계속해서 실행됩니다.
step i++ 각 반복의 body가 실행된 이후에 실행됩니다.

일반적인 반복문 알고리즘은 다음과 같습니다.

begin을 실행함
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ ...

begin이 한 차례 실행된 이후에, condition 확인과 body, step이 계속해서 반복 실행되죠.

반복문을 처음 배우신다면, 위 예시를 실행했을 때 어떤 과정을 거쳐 얼럿 창이 출력되는지 종이에 적어가며 공부해보세요. 이렇게 하면 반복문을 쉽게 이해할 수 있습니다.

정확히 어떤 과정을 거치는지는 아래 예시에서 확인할 수 있습니다.

// for (let i = 0; i < 3; i++) alert(i)

// begin을 실행함
let i = 0
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// i == 3이므로 반복문 종료
인라인 변수 선언

지금까진 ‘카운터’ 변수 i를 반복문 안에서 선언하였습니다. 이런 방식을 ‘인라인’ 변수 선언이라고 부릅니다. 이렇게 선언한 변수는 반복문 안에서만 접근할 수 있습니다.

 
 
for (let i = 0; i < 3; i++) {
  alert(i); // 0, 1, 2
}
alert(i); // Error: i is not defined

인라인 변수 선언 대신, 정의되어있는 변수를 사용할 수도 있습니다.

 
 
let i = 0;

for (i = 0; i < 3; i++) { // 기존에 정의된 변수 사용
  alert(i); // 0, 1, 2
}

alert(i); // 3, 반복문 밖에서 선언한 변수이므로 사용할 수 있음

구성 요소 생략하기

for문의 구성 요소를 생략하는 것도 가능합니다.

반복문이 시작될 때 아무것도 할 필요가 없으면 begin을 생략하는 것이 가능하죠.

예시를 살펴봅시다.

 
 
let i = 0; // i를 선언하고 값도 할당하였습니다.

for (; i < 3; i++) { // 'begin'이 필요하지 않기 때문에 생략하였습니다.
  alert( i ); // 0, 1, 2
}

step 역시 생략할 수 있습니다.

 
 
let i = 0;

for (; i < 3;) {
  alert( i++ );
}

위와 같이 for문을 구성하면 while (i < 3)과 동일해집니다.

모든 구성 요소를 생략할 수도 있는데, 이렇게 되면 무한 반복문이 만들어집니다.

for (;;) {
  // 끊임 없이 본문이 실행됩니다.
}

for문의 구성요소를 생략할 때 주의할 점은 두 개의 ; 세미콜론을 꼭 넣어주어야 한다는 점입니다. 하나라도 없으면 문법 에러가 발생합니다.

반복문 빠져나오기

대개는 반복문의 조건이 falsy가 되면 반복문이 종료됩니다.

그런데 특별한 지시자인 break를 사용하면 언제든 원하는 때에 반복문을 빠져나올 수 있습니다.

아래 예시의 반복문은 사용자에게 일련의 숫자를 입력하도록 안내하고, 사용자가 아무런 값도 입력하지 않으면 반복문을 '종료’합니다.

 
 
let sum = 0;

while (true) {

  let value = +prompt("숫자를 입력하세요.", '');

  if (!value) break; // (*)

  sum += value;

}
alert( '합계: ' + sum );

(*)로 표시한 줄에 있는 break는 사용자가 아무것도 입력하지 않거나 Cancel버튼을 눌렀을 때 활성화됩니다. 이때 반복문이 즉시 중단되고 제어 흐름이 반복문 아래 첫 번째 줄로 이동합니다. 여기선 alert가 그 첫 번째 줄이 되겠죠.

반복문의 시작 지점이나 끝 지점에서 조건을 확인하는 것이 아니라 본문 가운데 혹은 본문 여러 곳에서 조건을 확인해야 하는 경우, '무한 반복문 + break’ 조합을 사용하면 좋습니다.

다음 반복으로 넘어가기

continue 지시자는 break의 '가벼운 버전’입니다. continue는 전체 반복문을 멈추지 않습니다. 대신에 현재 실행 중인 이터레이션을 멈추고 반복문이 다음 이터레이션을 강제로 실행시키도록 합니다(조건을 통과할 때).

continue는 현재 반복을 종료시키고 다음 반복으로 넘어가고 싶을 때 사용할 수 있습니다.

아래 반복문은 continue를 사용해 홀수만 출력합니다.

 
 
for (let i = 0; i < 10; i++) {

  // 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
  if (i % 2 == 0) continue;

  alert(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}

i가 짝수이면 continue가 본문 실행을 중단시키고 다음 이터레이션이 실행되게 합니다(i가 하나 증가하고, 다음 반복이 실행됨). 따라서 alert 함수는 인수가 홀수일 때만 호출됩니다.

continue는 중첩을 줄이는 데 도움을 줍니다.

홀수를 출력해주는 예시는 아래처럼 생길 수도 있습니다.

 
 
for (let i = 0; i < 10; i++) {

  if (i % 2) {
    alert( i );
  }

}

기술적인 관점에서 봤을 때, 이 예시는 위쪽에 있는 예시와 동일합니다. continue를 사용하는 대신 코드를 if 블록으로 감싼 점만 다릅니다.

그런데 이렇게 코드를 작성하면 부작용으로 중첩 레벨(대괄호 안의 alert 호출)이 하나 더 늘어납니다. if 안의 코드가 길어진다면 전체 가독성이 떨어질 수 있습니다.

‘?’ 오른쪽엔 break나 continue가 올 수 없습니다.

표현식이 아닌 문법 구조(syntax construct)는 삼항 연산자 ?에 사용할 수 없다는 점을 항상 유의하시기 바랍니다. 특히 break나 continue 같은 지시자는 삼항 연산자에 사용하면 안 됩니다.

아래와 같은 조건문이 있다고 해봅시다.

if (i > 5) {
  alert(i);
} else {
  continue;
}

물음표를 사용해서 위 조건문을 아래와 같이 바꾸려는 시도를 할 수 있을겁니다.

(i > 5) ? alert(i) : continue; // 여기에 continue를 사용하면 안 됩니다.

이런 코드는 문법 에러를 발생시킵니다.

이는 물음표 연산자 ?를 if문 대용으로 쓰지 말아야 하는 이유 중 하나입니다.

break/continue와 레이블

여러 개의 중첩 반복문을 한 번에 빠져나와야 하는 경우가 종종 생기곤 합니다.

i와 j를 반복하면서 프롬프트 창에 (0,0)부터 (2,2)까지를 구성하는 좌표 (i, j)를 입력하게 해주는 예시를 살펴봅시다.

 
 
for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 여기서 멈춰서 아래쪽의 `완료!`가 출력되게 하려면 어떻게 해야 할까요?
  }
}

alert('완료!');

사용자가 Cancel 버튼을 눌렀을 때 반복문을 중단시킬 방법이 필요합니다.

input 아래에 평범한 break 지시자를 사용하면 안쪽에 있는 반복문만 빠져나올 수 있습니다. 이것만으론 충분하지 않습니다(중첩 반복문을 포함한 반복문 두 개 모두를 빠져나와야 하기 때문이죠 – 옮긴이). 이럴 때 레이블을 사용할 수 있습니다.

레이블(label) 은 반복문 앞에 콜론과 함께 쓰이는 식별자입니다.

labelName: for (...) {
  ...
}

반복문 안에서 break <labelName>문을 사용하면 레이블에 해당하는 반복문을 빠져나올 수 있습니다.

 
 
outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
    if (!input) break outer; // (*)

    // 입력받은 값을 가지고 무언가를 함
  }
}
alert('완료!');

위 예시에서 break outer는 outer라는 레이블이 붙은 반복문을 찾고, 해당 반복문을 빠져나오게 해줍니다.

따라서 제어 흐름이 (*)에서 alert('완료!')로 바로 바뀝니다.

레이블을 별도의 줄에 써주는 것도 가능합니다.

outer:
for (let i = 0; i < 3; i++) { ... }

continue 지시자를 레이블과 함께 사용하는 것도 가능합니다. 두 가지를 같이 사용하면 레이블이 붙은 반복문의 다음 이터레이션이 실행됩니다.

레이블은 마음대로 '점프’할 수 있게 해주지 않습니다.

레이블을 사용한다고 해서 원하는 곳으로 마음대로 점프할 수 있는 것은 아닙니다.

아래 예시처럼 레이블을 사용하는 것은 불가능합니다.

break label; // 아래 for 문으로 점프할 수 없습니다.

label: for (...)

break와 continue는 반복문 안에서만 사용할 수 있고, 레이블은 반드시 break이나 continue 지시자 위에 있어야 합니다.

요약

지금까지 세 종류의 반복문에 대해 살펴보았습니다.

  • while – 각 반복이 시작하기 전에 조건을 확인합니다.
  • do..while – 각 반복이 끝난 후에 조건을 확인합니다.
  • for (;;) – 각 반복이 시작하기 전에 조건을 확인합니다. 추가 세팅을 할 수 있습니다.

‘무한’ 반복문은 보통 while(true)를 써서 만듭니다. 무한 반복문은 여타 반복문과 마찬가지로 break 지시자를 사용해 멈출 수 있습니다.

현재 실행 중인 반복에서 더는 무언가를 하지 않고 다음 반복으로 넘어가고 싶다면 continue 지시자를 사용할 수 있습니다.

반복문 앞에 레이블을 붙이고, break/continue에 이 레이블을 함께 사용할 수 있습니다. 레이블은 중첩 반복문을 빠져나와 바깥의 반복문으로 갈 수 있게 해주는 유일한 방법입니다.



if 조건문으로 특정 조건일 때 코드 실행하기

조건문은 특정 조건일 때 코드를 실행하는 문법입니다. 프로그램을 만들다 보면 여러 가지 상황을 처리해야 하는 경우가 생기죠. 이때 조건문은 다양한 상황에 대처할 때 사용합니다.

먼저 실생활의 예를 들어보겠습니다. 만약 세탁기에 빨래를 넣고 돌렸다면 다음과 같은 조건문을 만들 수 있겠죠?

if 세탁 완료 소리가 울리면:
    빨래를 꺼내서 말린다.

다음과 같이 날씨에 따라 행동할 수도 있습니다.

if 비가 온다면:
    우산을 가지고 나간다.
 
if 날씨가 춥다면:
    코트를 입고 나간다.
 
if 날씨가 덥다면:
    반소매에 얇은 옷을 입고 나간다.

즉, 조건문을 사용하면 조건에 따라 다른 코드를 실행할 수 있습니다. 이번 유닛부터는 if 조건문의 다양한 사용 방법을 알아보겠습니다.

참고 | 의사 코드

프로그래밍이나 컴퓨터 이론을 공부하다 보면 의사 코드(pseudo code)라는 말을 접하게 됩니다. 의사 코드는 실제 프로그래밍 언어가 아닌 사람의 언어로 프로그래밍 언어를 표현한 것입니다. 보통 특정 프로그래밍 언어를 사용하지 않고 알고리즘이나 컴퓨터 명령을 기술할 때 사용합니다.

x = 10    # 파이썬 코드
변수 x에 10 할당    # 한글로 표현한 의사 코드

앞에서 if 조건문을 설명할 때 "if 비가 온다면", "우산을 가지고 나간다."도 일종의 의사 코드입니다.

 

 

if 조건문 사용하기

if 조건문은 if에 조건식을 지정하고 :(콜론)을 붙이며 다음 줄에 실행할 코드가 옵니다. 이때 실행할 코드는 반드시 들여쓰기를 해야 합니다.

if 조건식:
     코드

먼저 IDLE의 파이썬 셸에서 if 조건문을 사용해보겠습니다.

>>> x = 10
>>> if x == 10:
...      print('10입니다.')
... 
10입니다.

만약 if 다음 줄에서 들여쓰기를 하지 않으면 들여쓰기 에러가 발생합니다. 이 항상 이 부분을 주의해주세요.

>>> x = 10
>>> if x == 10:
... print('10입니다.') 
  File "<stdin>", line 2
    print('10입니다.')
        ^
IndentationError: expected an indented block 

참고로 IDLE의 파이썬 셸에서는 자동으로 들여쓰기가 되지만, 콘솔(터미널, 명령 프롬프트)에서 실행한 파이썬 셸에서는 자동으로 들여쓰기가 되지 않으므로 반드시 들여쓰기를 해줍니다.

13.1.1  if 조건문의 기본 형태와 실행 흐름 알아보기

이제 if 조건문을 자세히 알아보겠습니다. 파이썬에서 if 조건문은 if 조건식: 형식으로 사용하며 그다음 줄에는 들여쓰기를 한 뒤 조건식이 만족할 때 실행할 코드를 넣습니다. 특히 이 조건식이 만족할 때 실행할 코드를 if 본문(if body)이라고 부릅니다.

▼ 그림 13-1 if 조건문의 기본 형태

여기서는 변수 x에 10을 할당한 뒤 if 조건문으로 x가 10과 같은지 검사하였습니다. 조건식은 x == 10과 같은 형식으로 지정해주는데 ==은 두 값이 "같을 때" 라는 뜻입니다.

즉, if x == 10:은 x가 10과 같은지 비교한 뒤 같으면 다음에 오는 코드를 실행하라는 뜻이 됩니다. 따라서 x는 10이고 조건식을 만족하므로 그다음 줄의 print가 실행되어 '10입니다.'가 출력됩니다.

 

if 조건문의 실행 흐름을 그림으로 표현하면 다음과 같은 모양이 됩니다. 코드와 실행 흐름을 비교해보세요.

▼ 그림 13-2 if 조건문의 실행 흐름

보통 if의 조건식이 만족하면 참( True), 만족하지 않으면 거짓(False)이라고 부릅니다.

13.1.2  if 조건문을 사용할 때 주의할 점

if 조건문을 사용할 때 주의할 점이 있는데 파이썬에서는 =을 할당으로 사용하고 있으므로 값을 비교할 때는 =을 두 개 붙여서 ==로 사용해야 합니다. 자주 틀리는 부분이니 if 안에서 ==을 사용했는지 반드시 확인하세요. 다음과 같이 if에 =을 사용하면 문법 에러가 발생합니다.

>>> if x = 10: 
  File "<stdin>", line 1
    if x = 10:
         ^
SyntaxError: invalid syntax 

조건식 끝에 :을 빠뜨리는 실수도 자주하니 :도 확인해주세요. 다음은 조건식 끝에 :을 빠뜨렸을 때의 모습입니다.

>>> if x == 10  
  File "<stdin>", line 1
    if x == 10
             ^
SyntaxError: invalid syntax 

문법 에러가 발생하면 콘솔에서는 잘못된 코드 아래에 ^가 표시되고, IDLE에서는 빨간색으로 표시되므로 자신이 실수한 부분을 쉽게 찾을 수 있습니다.

13.1.3  if 조건문에서 코드를 생략하기

이번에는 if 조건문에 조건식만 작성하고 코드를 생략하는 방법을 알아보겠습니다.

>>> x = 10
>>> if x == 10:
...     pass
...
>>> 

if 다음 줄에 pass라는 특별한 키워드를 넣었습니다. 여기서 pass는 아무 일도 하지 않고 그냥 넘어간다는 뜻입니다. 파이썬에서는 if 다음 줄에 아무 코드도 넣지 않으면 에러가 발생하므로 if 조건문의 형태를 유지하기 위해 pass를 사용합니다.

pass는 아무 일도 하지 않는 코드라서 의미가 없을 것 같지만 나중에 작성해야 할 코드를 표시할 때 사용할 수 있습니다. 즉, 다음과 같이 pass만 넣고 나중에 할 일은 주석으로 남겨놓는 방식입니다.

if x == 10:
    pass    # TODO: x가 10일 때 처리가 필요함
참고 | TODO

TODO는 해야 할 일이라는 뜻인데 보통 주석에 넣습니다. 이렇게 TODO를 넣어 두면 검색으로 쉽게 찾을 수 있죠. 그래서 프로그래머들은 주석에 TODO 이외에도 FIXME, BUG, NOTE 등과 같이 코드는 아니지만 일관된 주석을 사용합니다.

 

 

1. 파이썬 함수란 무엇인가.


  입력값에 따라 결과가 다를수는 있지만 로직 자체는 같은 경우에 하나의 포장지 안에 넣어서 계속 재사용할 수 있게 만들어 놓은것을 함수라 합니다

  그렇기 때문에 하나의 로직을 비효율적으로(?) 복붙하여 계속 쓰지 않고, 함수로 묶어서 관리하게 되면 좀더 편리하게 사용할 수 있습니다. 그렇기 때문에 함수를 사용하는 것 입니다.

자동차도 그래요. 자동차를 만든다고할때 공장에서 바퀴 담당하는 부분이 있는데 그 바퀴를 일일히 하나하나 앞바퀴 왼쪽 오른쪽, 뒷바퀴 왼쪽 오른쪽을 하나씩 각자 만든다고 할때 얼마나 비효율적일까요?

이걸 공장에서 자동화해서 하나의 기계에서 똑같은 바퀴를 4개 뽑아내잖아요. 그게 함수와 동일한것 입니다. 자동화한 기계. 즉 똑같은 결과물을 내는 기계가 함수라 할수 있고, 바퀴는 그 반환값이라 할 수 있습니다. 만약에 그 기계에서 바퀴의 크기 (몇인치)를 입력해서 하나의 기계에서 다른 사이즈의 바퀴도 만들 수 있다면 그건 매개변수가 있는 함수라고도 할 수 있습니다.

파이썬에서 함수를 알려주기 위해서는 def 라는 키워드를 사용합니다. def를 사용하면 파이썬이 "아 이제 함수를 정의하겠구나"하고 인식하게 됩니다.
그리고 함수에는 입력값이 있고 출력값이 있는데요.
이거는 위에서 말했듯이 자동차 바퀴 몇인치를 원하는가? 이게 입력값이 되고, 그 입력값에 따라서 딱 맞게 바퀴가 나오는걸 출력값이라 합니다.
함수에 따라서 입력값이 있는 함수도 있고, 출력값이 있는 함수도 있으며, 둘다 있거나 둘다 없는 함수도 있습니다.

 

2. 파이썬에서 다양한 함수의 모양에 대해서


입력도 없고 반환값도 없는 함수

def 함수명() :
   수행문장
   ...

이런식으로 def뒤에 함수명이 오고, 소괄호 ()를 붙인 다음 콜론(:)을 붙여줍니다.
그 다음에 원하는 문장을 함수 아래에 들여쓰기 하여서 사용하면 됩니다.
예제로 한번 볼까요?

1
2
def func1():
    print('BlockDMask')
cs

이런식으로 func1이라는 함수를 정의 하였습니다. 함수를 정의만 한다고 사용되지 않습니다.
함수를 한번 사용해볼까요? 아, 코드에서 함수를 사용한다는 것은 함수를 호출한다고 합니다. 그럼 함수를 한번 호출해볼까요?
함수를 호출하는 방법은 함수의 이름과 () 소괄호까지 입력을 하면 호출이 됩니다.
이왕 함수를 만들어본거 한 세번정도 호출해 보겠습니다.

1
2
3
4
5
6
7
def func1():
    print('BlockDMask')
 
# 함수 호출
func1()
func1()
func1()
cs

이런식으로 작성을 하면 함수가 호출되면서 함수 내부의 로직이 작동하게 됩니다.
우리는 func1이라는 함수 내부에 print("BlockDMask")라는 명령을 집어 넣음으로써 함수가 호출될때 마다 "BlockDMask"라는 문자열이 출력되도록 하였습니다. 어디 결과값을 한번 볼까요?


이렇게 해서 결과값을 볼 수 있습니다.
만약에 함수가 아니고 비효율적(?)으로 똑같은걸 반복한다 한다면 print(" ")를 3번 반복해서 작성해야합니다.
지금정도야 한줄이니 괜찮지만 함수 내부 로직이 여러줄이고 복잡하게 된다면, 그걸 일일히 N번 반복해서 작성한다 한다면 가독성은 물론이고 코드가 정말 난잡해 질것 입니다.

 

 

 

입력만 있는 함수

입력이라는게 아까 예시를 들은 바퀴의 몇인치? 인거라 생각하면됩니다.
파이썬에서 함수의 입력값을 매개변수 혹은 인수라고 부릅니다. 면밀히 말하면 다른 용어이지만, 뜻이 거의 통하므로 동일하다고 봐도 무방합니다. 하지만 면접같은데서 물어본다면 구분해서 답변할 수 있으면 좋겠죠?

매개변수와 인수가 무엇인가를 설명하기 전에 입력이 있는 함수의 모양을 먼저 보겠습니다.

def 함수이름(매개변수1, 매개변수2, ... ):
    수행문장

이런식으로 괄호 안에 매개변수라는게 생겼습니다. 이게 우리가 함수를 호출할때 특정 값을 넣어주기 위해서 통로를 뚫은거라고 생각하면 좋겠습니다.
이렇게 함수를 정의했을때 함수 이름 옆에 있는 변수들을 매개변수라 부릅니다.
일단 예시를 한번 볼까요?

1
2
3
4
5
6
7
def func2(a, b):
    print(f'{a} 곱하기 {b} = {a * b}')
 
func2(12)
func2(13)
func2(24)
 
cs

짠, 이렇게 함수의 이름옆에 매개변수를 두개 정의하였습니다.
이 매개변수를 이용해서 함수 내부에서 자유롭게 로직을 만들 수 있습니다.
그리고 호출부를 보면 func2( ) 괄호 안에 1, 2 이렇게 값들을 집어 넣은것을 볼 수 있습니다.
우리가 정의한 함수 모양에 맞게 변수를 두개 넣어준것인데 이때 이 변수들을 인수라고 부릅니다.

매개변수가 2개인 함수를 정의했고
그 함수를 호출할때는 인수, 인자값 2개를 넣어서 함수를 호출하여 함수가 잘 작동하는 것을 볼 수 있습니다.

매개변수는 1개부터 시작해서 N개 까지 정의해서 본인 입맛에 맞게 함수를 정의하면 됩니다.

 

반환값만 있는 함수

이번에는 값을 뱉는 함수 즉 반환 값이 있는 함수에 대해서 알아보려합니다.
함수를 호출하면 함수 내부에서 어떤 로직을 돌아서 그 결과값을 함수 밖으로 보내주는걸 반환 값이라 하는데요. 모양은 아래와 같습니다.

def 함수이름():
    수행문장
    return 반환값

이처럼 return 을 작성하고 그 뒤에 함수 밖으로 보낼 반환값을 적습니다. 바로 예제로 이해해볼까요?

1
2
3
4
5
6
7
def func3():
    return "abcdefg"
 
 
= func3()
print(a + "GG")
 
cs

이렇게 return 뒤에 반환하고자 하는 값을 넣으면 함수 밖에서 값을 받아서 사용할 수 있습니다.
func3() 함수를 호출하고 그 앞에 = 을 통해서 변수를 선언하여 그 변수에 반환값을 받는것 입니다. 그러면 우리는 그 변수 a를 통해서 func3()의 반환 값을 이용할 수 있습니다.

 

입력, 반환값이 둘다 있는 함수

위에서 매개변수, 반환값 둘다 배웠죠? 그 두개가 함께 있는 경우입니다. 제일 흔하게 볼 수 있는 함수의 모양입니다.

def 함수이름(매개변수1, 매개변수2 ...):
    수행문장
    return 반환값

바로 예제로 들어가 보겠습니다.
곱셈을 해주는 함수를 만들어보겠습니다.

1
2
3
4
5
6
7
def func4(a, b):
    return a * b
 
 
= func4(39)
print(c)
 
cs

매개변수를 2개를 받아서 그 매개변수를 곱한 값을 반환해주는 함수 func4를 만들었습니다.
함수를 호출할때 매개변수를 집어넣고 그 반환값을 받을 변수까지 만들어 두면 완벽하게 함수를 사용할 수 있습니다.
그럼 3, 9를 인자로 집어 넣었으니 반환값은 3 * 9인 27을 반환할 것 입니다.

 

 

 

3. 파이썬 함수 예제


구구단. 함수를 사용하지 않았을 때

1
2
3
4
5
6
7
8
9
10
11
12
# 구구단 no 함수 버전
 
# 구구단 출력
for i in range(110):
    print(f'{2} x {i} = {2 * i}')
for i in range(110):
    print(f'{3} x {i} = {3 * i}')
 
# ...
for i in range(110):
    print(f'{9} x {i} = {9 * i}')ㅁ
 
cs

구구단. 함수를 사용할때

1
2
3
4
5
6
7
8
9
10
11
# 구구단 함수 버전
def gugudan(num):
    for i in range(110):
        print(f'{num} x {i} = {num * i}')
 
# 구구단 출력
gugudan(2)
gugudan(3)
# ...
gugudan(9)
 
cs

이처럼 매개변수(인자)만 변경하면 똑같이 행동하는 로직을 함수로 묶어버리면 코드가 훨씬 깔끔해 질 수 있고, 우리는 같은 코드를 작성하지 않아도 됩니다.
비슷한 행위, 같은 행위가 반복된다면 함수로 만들어서 편리하게 사용해 보세요.

 

 

 

4. 파이썬 함수 응용 (디폴트 매개변수, 매개변수 N개, 튜플 반환)


디폴트 매개변수

우리가 함수에서 매개변수를 정의할때 그 매개변수의 디폴트값 즉 기본값을 지정해 줄수 있습니다. 그러면 우리는 그 매개변수를 사용하지 않을때. 그 디폴트 값이 해당 변수에 들어가서 함수 내부에서 사용이 됩니다. 

def 함수이름 (매개변수1, 매개변수2 = A, 매개변수3 = B):
    수행문장
    return 반환값 

당연한 이야기지만 return 반환값은 없어도 무방합니다. 매개변수의 개수도 상관없는거 이제는 이해하시겠죠?
위 매개변수를 보면 매개변수를 선언하는곳에 " = A " 이렇게 값이 지정되어있습니다. 이거는 매개변수 2가 들어오지 않으면 함수자체 내부에서 A라는 값으로 지정해서 사용하겠다는 뜻입니다. 매개변수 3번도 마찬가지 입니다.

주의할점은 제가 일부로 저 함수의 모양을 매개변수2, 3에만 디폴트 매개변수를 선언했는데요. 그 이유는 디폴트 매개변수는 뒤에서부터 선언해야 가능하기 때문입니다. 그게 무슨말인가 하면
def 함수이름 (매개변수1 = C, 매개변수2, 매개변수3) 이렇게 뒤에있는 매개변수 2,3번을 디폴트 매개변수로 지정해주지 않고 건너뛰어서 매개변수1만 디폴트로 지정해주는것은 불가능 하다는 것 입니다.

이게 왜 불가능한지는 함수 예제를 통해서 보여드리겠습니다.
아마 보시면 "아~ 컴퓨터도 무슨 매개변수를 말하는건지 헷갈리겠구나 "

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 디폴트 파라미터 예제 1
def func1(a, b=5, c=10):
    return a + b + c
 
 
func1(123)  # 1 + 2 + 3
func1(12)  # 1 + 2 + 10
func1(1)  # 1 + 5 + 10
# func1() error
 
 
# 디폴트 파라미터 예제 2
def func2(a=10, b=20):
    return a + b
 
 
func2(12)  # 1 + 2
func2(1)  # 1 + 20
func2()  # 10 + 20
 
 
# 아래는 잘못된 디폴트 파라미터
# 디폴트 파라미터는 뒤에서 부터 해야한다.
# 그래야 인수가 비었을때 판단이 가능.
def func3(a = 10, b, c): # error
    return a + b + c
 
func3(12)    # 1이 a이고 2가 b에 들어가는거겠지? 
               # 뭘 원하는거야? error
func3( , 12# 뭐 이렇게 호출해야하나? error
 
cs

예제 1이랑 2를 보면 디폴트 파라미터가 있으면 인수를 생략해도 함수가 잘 작동하는것을 볼 수 있습니다.

하지만 예제 3번을 보면 디폴트 파라미터를 뒤에서부터 설정하지 않고 뒤에 비어있고, 앞에 존재한다면. 이는 오류 입니다. 우리가 함수를 호출할때는 ( ) 괄호안에 인자를 순서대로 넣게 되는데, 디폴트 파라미터로 선언했다 해서 비어서 넣는다는것은 불가능합니다. 그렇기 때문에 디폴트 파라미터는 뒤에서부터 선언해야합니다.

 

매개변수 N개

매개변수라는게 함수를 정의할때 ( ) 괄호 안에 들어갈 변수들인데, 두개면 두개 세개면 세개 이렇게 매개변수의 개수를 딱 알면 좋은데, 이 입력하는 매개변수가 몇개가 될지 모르는 경우에는 어떻게 해야할지 감이 오시나요?

파이썬에서는 이런 경우를 대비해서 몇개가 오든 상관없으니 매개변수로 넘길 수 있는 방법을 만들어놓았습니다.

이런걸 다른언어에서는 인자의 개수가 변한다 하여, 가변인자라고도 하는데. 파이썬에서는 따로 명칭이 없는거같으니 여러개의 입력값 즉 N개의 입력값을 받는 함수라 하겠습니다. 모양은 아래와 같습니다.

def 함수이름 (*매개변수):
    수행문장
    return 반환값

당연하게도 본인이 만들 함수 의도에 따라서 "return 반환값"이 없어도 상관없습니다.
중요하게 볼것은 *매개변수 이것입니다.
예제로 한번 보겠습니다.
아래 예제는 매개변수로 넘어온 값들을 모두 더한 값을 반환하는 함수 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def func6(*args):
    a = 0
    for i in args:
        a = a + i
    return a
 
 
= func6(12)
print(b)
 
= func6(2345)
print(c)
 
= func6(123454321)
print(d)
 
= func6()
print(e)
 
cs

이렇게 매개변수에 *args를 선언하면 함수를 호출할때 인자의 개수가 0개여도, 2개여도 4개여도 N개여도 상관없이 함수에서 다 받을 수 있습니다.
이게 어떻게 가능하냐면 *매개변수로 넘어온걸 파이썬은 그걸 튜플 타입으로 변환하여서 함수 내부에 전달해줍니다. 그럼 튜플 타입을 이용하는 방법을 통해서 우리는 내부 로직을 구현할 수 있는 것 입니다.
args는 아규먼트(arguments)의 축약어 이며, 보통 이렇게 N개의 매개변수를 표현할때 args를 사용합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

================================================================================================

솔직히 제가 적은건 지금이거 하나뿐이지만 그래도 파일 입출력, 클래스, 모듈, 예외처리 내용은 넣겠습니다.

 

 

 

파일입출력

1. 파이썬 파일 생성 open, close 함수


파이썬에서 파일을 생성할때에 사용하는 함수는 open() 함수 입니다.
open 함수는 파이썬 기본 내장함수 입니다.
이 함수이름은 open이지만 옵션에 따라서 파일을 생성 하고 열기 인 경우, 읽기만을 위한 열기, 쓰기위한 열기 등 여러 옵션들이 있습니다.

1-1) open() 함수 기본 모양

def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): 

정의를 보면 이렇게 복잡하게 되어있지만 우리는 앞에 두가지만 알아보려 합니다. 마지 open 함수의 모양이 원래 아래와 같다고 생각해도 무방합니다. 뒤에있는 인자들은 저희같은 초보는 아직 몰라도 될것 같습니다.
def open(filemode='r'):

open 함수는 파일을 열고 해당 파일에 대한 스트림을 반환합니다.
파일 열기를 실패했을때는 Error를 발생시킵니다.

인자를 보면

첫번째 인자 file
- 파일 경로를 집어 넣습니다.

두번째 인자 mode
- 파일이 열리는 옵션, 모드 입니다. 이 모드에 따라 읽기 용인지 파일을 생성하고 쓸것인지 등이 정해집니다.
- 'r' : 기본값으로 정해져 있으며 파일을 읽기 위한 옵션 입니다.
- 'w' : 쓰기모드이며 파일에 내용을 쓸 때 사용하는 옵션 입니다. 만약 이미 파일이 존재하면 커서를 맨 앞으로 돌리면서 뒤에 내용을 다 잘라내기 때문에 내용이 사라질 수 있습니다. 파일이 존재하지 않는다면 새롭게 파일을 생성합니다.
- 'a' : 쓰기모드이며 파일에 내용을 쓸 때 사용하는 옵션입니다. w 옵션과는 달리 이미 파일이 존재하면 그 파일의 끝에 커서가 존재하고, 그 뒤에 이어쓰기가 가능합니다.
즉, 파일 내용을 잘라내지 않고 이어서 쓸 수 있습니다.
- 'x' : 파일이 없으면 파일을 생성하고 쓰기모드로 열립니다. 만약 파일이 있으면 에러를 발생시킵니다.

- 'b' : 바이너리 모드 입니다.
- 't' : 텍스트 모드 입니다. (기본값)

두번째 인자는 'r', 'w', 'a', 'x' 와 같이 't'나 'b'를 따로 쓰지 않으면 기본적으로 텍스트 모드인 't'로 열리게 됩니다. 즉, 읽기위해 'r'을 넣는다는것은 'rt'를 넣는것과 동일한 뜻 입니다.
만약 파일을 읽기모드인데 바이너리 모드로 열고싶다면 'rb' 이런식으로 집어 넣으면 됩니다.
따로 표기 하지않으면 기본적으로 텍스트 모드인 't'로 열립니다.

오픈한 파일을 닫으려면 파일 닫기 함수인 close()함수를 이용해야합니다.

1-2) open() 함수 예제

1
2
3
4
5
6
7
8
9
10
# 파일 열기
= open('C:/Test/t1.txt''w')
 
# 파일에 텍스트 쓰기
f.write('blockdmask blog')
f.write('\npython open func')
 
# 파일 닫기
f.close()
 
cs

open('경로/파일이름', '모드') 함수를 이용해서 'w'모드로 열게되면 파일이 존재하지 않기 때문에 해당 경로에 't1.txt'라는 파일이 생성된 후에 변수 f에 파일 스트림이 들어오게 됩니다.

해당 파일 스트림을 이용해서 우리는 파일에 내용을 쓰고, 읽고 할 수 있습니다.
예제에서는 write(' ') 함수를 통해서 파일에 문자열을 집어 넣어주었습니다.
파일을 뜯고 씹고 맛본 다음에는 파일을 얌전히 닫아줍니다. 이때 사용하는 함수는 close() 함수 입니다.

결과를 보면 t1.txt라는 파일이 생성되고 그 안에 문자열이 잘 들어가있는걸 볼 수 있습니다.

 

**주의
파일 객체는 반드시 열었으면(open) 닫아야(close)합니다.
오픈한 파일을 닫지 않는다면 데이터가 날라갈 수 있습니다. 

 

 

 

2. 파이썬 파일 쓰기 write, writelines 함수


파일을 열었으면 파일 스트림을 이용해서 파일에 텍스트를 쓸 수 있습니다.
그 때 사용하는 함수가 write 함수, writelines 함수 입니다.

2-1) 파이썬 write 함수

IO.write("문자열")

write 함수는 매개변수로 파일에 넣을 문자열을 받습니다.

1
2
3
4
5
6
7
8
9
10
# 파일 w 모드로 열기 (파일 새로 만듦)
= open('C:/Test/t2.txt''w')
 
# write 함수를 이용해서 파일에 텍스트 쓰기
f.write('write write write\n')
f.write('파이썬 파일 입출력 포스팅\n')
 
# 파일 닫기
f.close()
 
cs

f = open('파일경로/파일이름', 'w')
- 해당 코드를 실행하면 t2.txt 파일이 생성됩니다. w모드로 열었기 때문에 파일이 있으면 덮어씌워지고 내용은 싹 지워집니다. 파일이 없으면 파일이 생성됩니다.

f.write('')
- 파일을 열고 파일스트림을 이용해서 write 함수를 호출 했습니다.
내용을 보시면 개행문자(\n)을 직접 입력해준걸 볼 수 있습니다. 이렇게 개행이나 탭 등은 우리가 직접 입력을 해주어야합니다.

f.close()
- 파일을 열었으면 꼭 닫아야죠

결과를 보면 파일에 우리가 입력한 내용이 잘 들어가있는걸 볼 수 있습니다.

 

2-2) 파이썬 writelines 함수

IO.writelines(List["문자열1", "문자열2", ... ])

write 함수는 매개변수로 파일에 넣을 문자열 리스트를 받습니다.

아까 생성했던 t2.txt에 이어서 파일에 문자열을 집어 넣어보겠습니다. 이미 있는 파일이고 해당 파일에 이어서 써야하니 'a'모드로 열어야겠죠?
 
1
2
3
4
5
6
7
8
9
10
11
12
# 파일 a 모드로 열기 (이미 있는 파일에 이어서 쓰기)
= open('C:/Test/t2.txt''a')
 
# writelines 함수를 이용해서 파일에 문자열들 쓰기
f.writelines(['a''b''123''456''abcdefg''\n'])
 
# writelines 함수에 join 을 이용해서 문자열들에 자동 개행 넣기
f.writelines('\n'.join(['BlockDMask''python''blog']))
 
# 파일 닫기
f.close()
 
cs

f = open('파일경로/파일이름', 'a')
- 'a' 모드로 파일을 열었기 때문에 파일이 있으면 해당 파일의 맨 끝으로 가서 이어서 쓸 수 있습니다. 파일이 없으면 파일이 생성됩니다.

f.writelines(list[문자열1, 문자열2, ...]
- 파일을 열고 파일스트림을 이용해서 writelines 함수를 호출 했습니다.
내용을 보시면 이번에도 문자열 리스트의 맨 끝에 개행문자(\n)을 직접 입력해준걸 볼 수 있습니다.
그럼 우리는 join 함수를 이용해서 개행을 문자열 리스트 사이사이에 넣어주는 걸 응용할 수 있습니다. '\n'.join([ 문자열 리스트 ]) 이런식으로 하면 위 결과창과 같이 사이사이 개행이 잘 들어가 있는걸 볼 수 있습니다.

f.close() 
- 파일을 열었으면 꼭꼭 닫아야죠22

 

 

 

3. 파이썬 파일 읽기 read, readline, readlines, seek, tell 함수


파일을 읽는 함수 read, readline, readlines 세가지와 파일 위치 관련함수 seek, tell 함수 이렇게 한번 알아보겠습니다.

3-1) read, readline, readlines함수 설명

IO.read(n)

read(n) 함수는 파일 스트림으로 부터 해당 위치의 문자 n개를 읽어오는 함수 입니다. n바이트를 읽어오는 것 입니다. read() 이렇게 아무것도 넣지 않으면 모든 문자를 읽어오게 됩니다.
반환 : 문자N개를 반환합니다.

IO.readline()

readline() 함수는 함수 이름에서 알 수 있듯이 파일 스트림으로 부터 해당 위치에서부터 한 줄, 한 라인의 문자열을 읽어옵니다. 즉 '\n' 개행이 있을때 까지 문자열을 읽어옵니다.
반환 : 문자열을 반환합니다.

IO.readlines()

readlines() 함수는 해당 위치에서부터 파일의 모든 문자열을 읽어옵니다. (개행 포함)
반환 : List[문자열]을 반환합니다. 리스트로 반환됩니다.

 

3-2) seek, tell 함수 설명

우리가 워드나 메모장 하다못해 어딘가에 글을 쓸때 항상 세로줄로 | 깜빡깜빡 하는 커서가 있을것 입니다. 이것은 현재 글을 쓸 위치를 알려주는것인데, 이것을 기준으로 글이 써지게 됩니다.

파이썬에서 파일을 다룰때에도 이러한 커서의 위치에 따라서 그 위치에 문자를 쓰고, 그 위치에서 문자를 읽어오게 됩니다. seek, tell 함수는 이 커서의 위치를 이동하고, 커서의 현재 위치를 가지고 오는 함수 입니다.

IO.seek(위치)

seek 함수는 해당 위치로 파일의 커서를 옮깁니다. 파일의 맨 처음 위치는 0 입니다.

IO.tell()

tell 함수는 현재 커서의 위치를 반환하는 함수입니다.

 

3-3) 파일 읽기 함수 예제

우선 txt파일을 준비해줍니다. 파일 쓰기 예제에서 사용했던 t2.txt을 읽어오겠습니다.
현재 t2.txt 파일은 이러한 상태입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 파일 r 모드로 열기
= open('C:/Test/t2.txt''r')
 
# read() 함수 이용해서 하나씩 읽어오기
print('\n1. read()')
print(f'위치 : {f.tell()}')
 
s1 = f.read(1)
print(s1)
 
# readline() 함수 이용해서 한 라인씩 읽어오기
print('\n2. readline()')
print(f'위치 : {f.tell()}')
 
s2 = f.readline()
print(s2)
 
# readlines() 함수 이용해서 모두 읽어오기
print('\n3. readlines()')
print(f'위치 : {f.tell()}')
 
s3 = f.readlines()
print(s3)
 
# 맨 처음 위치로 가서 한줄 읽기
print('\n4. seek(0), readline()')
 
f.seek(0)
print(f'위치 : {f.tell()}')
 
s4 = f.readline()
print(s4)
 
 
# 파일 닫기
f.close()
 
cs

1. read()를 보면 파일 커서(=파일포인터)의 위치가 0에서 read()를 한번 호출하니 파일로 부터 "w"를 읽어와서 반환 하는 것을 볼 수있습니다.

2. readline()을 보면 앞서 사용했던 read() 함수에 의해서 파일 커서의 위치가 1로 변경된걸 알 수 있습니다. 그 상태에서 readline() 함수를 이용하니
"write write write" 문자열에서 1개 이동한 위치에서 읽어오기 때문에 "rite write write" 를 읽어온걸 알 수 있습니다.

3. realines() 를 보면 앞서 사용했던 realine 때문에 파일 커서의 위치가 바뀐걸 볼 수 있습니다. 그 상태에서 파일의 끝까지 문자들을 읽어오고, 개행까지 포함해서 리스트로 변경해주는걸 볼 수 있습니다.

4. seek(0) 함수를 이용해서 파일 커서의 위치를 맨 앞으로 옮기고 readline() 함수를 이용해서 한 라인의 문자열을 읽어 들였습니다. "write write write" 문자열을 읽어온것을 확인 할 수 있습니다.

 

 

 

 

 

 

클래스 사용하기

​클래스는 객체를 표현하기 위한 문법입니다. 예를 들어 게임을 만든다고 하면 기사, 마법사, 궁수, 사제 등 직업별로 클래스를 만들어서 표현할 수 있습니다.

▼ 그림 34-1 게임 캐릭터

물론 집, 자동차, 나무 등도 클래스로 표현할 수 있습니다. 특히 프로그래밍에서는 현실 세계에 있는 개념들뿐만 아니라 컴퓨터 안에서만 쓰이는 개념들도 클래스로 만들어서 표현합니다. 웹 브라우저에서 내용이 길어지면 보이는 스크롤 바, 프로그램에서 주로 볼 수 있는 버튼, 체크 박스 등이 대표적입니다.

▼ 그림 34-2 컴퓨터 안에서만 쓰이는 스크롤 바, 버튼, 체크박스

지금까지 나온 기사, 마법사, 궁수, 사제, 집, 자동차, 나무, 스크롤 바, 버튼, 체크 박스처럼 특정한 개념이나 모양으로 존재하는 것을 객체(object)라고 부릅니다. 그리고 프로그래밍으로 객체를 만들 때 사용하는 것이 클래스입니다.

그럼 게임의 기사 캐릭터를 클래스로 표현하려면 무엇이 필요할까요? 간단합니다. 일단 게임 캐릭터는 체력, 마나, 물리 공격력, 주문력 등이 필요합니다. 그리고 기사 캐릭터는 칼로 베기, 찌르기 등의 스킬이 있어야 합니다.

 

여기서 체력, 마나, 물리 공격력, 주문력 등의 데이터를 클래스의 속성(attribute)이라 부르고, 베기, 찌르기 등의 기능을 메서드(method)라고 부릅니다.

▼ 그림 34-3 클래스의 속성과 메서드

이렇게 프로그래밍 방법을 객체지향(object oriented) 프로그래밍이라고 합니다. 객체지향 프로그래밍은 복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합해서 문제를 해결합니다. 따라서 현실 세계의 복잡한 문제를 처리하는데 유용하며 기능을 개선하고 발전시킬 때도 해당 클래스만 수정하면 되므로 유지 보수에도 효율적입니다.

지금까지 숫자 1, 2, 3 문자 'a', 'b', 'c', 리스트, 딕셔너리 등을 조합해서 프로그램을 만들었는데 사실 파이썬에서는 이 모든 것이 객체입니다. 이번에는 클래스를 사용해서 객체를 표현하고 만드는 방법을 알아보겠습니다.

34.1 클래스와 메서드 만들기

클래스는 class에 클래스 이름을 지정하고 :(콜론)을 붙인 뒤 다음 줄부터 def로 메서드를 작성하면 됩니다. 여기서 메서드는 클래스 안에 들어있는 함수를 뜻합니다.

클래스 이름을 짓는 방법은 변수와 같습니다. 보통 파이썬에서는 클래스의 이름은 대문자로 시작합니다. 그리고 메서드 작성 방법은 함수와 같으며 코드는 반드시 들여쓰기를 해야 합니다(들여쓰기 규칙은 if, for, while과 같습니다). 특히 메서드의 첫 번째 매개변수는 반드시 self를 지정해야 합니다.

class 클래스이름:
    def 메서드(self):
        코드

이제 간단한 사람 클래스를 작성해보겠습니다.

>>> class Person:
...     def greeting(self):
...         print('Hello')
...
에러

SyntaxError: invalid syntax: 클래스와 메서드의 형식이 맞지 않아서 발생하는 구문 에러입니다. 메서드에서 ( )(괄호)의 짝이 맞는지, :(콜론)을 빠뜨리지 않았는지 확인해주세요.

TypeError: ... takes 0 positional arguments but 1 was given: 메서드의 첫 번째 매개변수를 self로 지정하지 않아서 발생하는 에러입니다. 메서드의 첫 번째 매개변수가 self인지 확인해주세요.

그럼 이 클래스를 사용해봐야겠죠? 다음과 같이 클래스에 ()(괄호)를 붙인 뒤 변수에 할당합니다.

  • 인스턴스 = 클래스()
>>> james = Person()

Person으로 변수 james를 만들었는데 이 james가 Person의 인스턴스(instance)입니다. 클래스는 특정 개념을 표현만 할뿐 사용을 하려면 인스턴스를 생성해야 합니다.

 

 

 

 

 

모듈

모듈이란 함수나 변수 또는 클래스를 모아 놓은 파이썬 파일이다. 모듈은 다른 파이썬 프로그램에서 불러와 사용할 수 있도록 만든 파이썬 파일이라고도 할 수 있다. 우리는 파이썬으로 프로그래밍을 할 때 매우 많은 모듈을 사용한다. 다른 사람들이 이미 만들어 놓은 모듈을 사용할 수도 있고 우리가 직접 만들어 사용할 수도 있다. 여기에서는 모듈을 어떻게 만들고 사용할 수 있는지 알아본다.

 

모듈 만들기

모듈에 대해 자세히 살펴보기 전에 간단한 모듈을 한번 만들어 보자.

# mod1.py
def add(a, b):
    return a + b

def sub(a, b): 
    return a-b

위와 같이 add와 sub 함수만 있는 파일 mod1.py를 만들고 C:\doit 디렉터리에 저장하자. 이 mod1.py 파일이 바로 모듈이다. 지금까지 에디터로 만든 파이썬 파일과 다르지 않다.

파이썬 확장자 .py로 만든 파이썬 파일은 모두 모듈이다.

모듈 불러오기

우리가 만든 mod1.py 파일, 즉 모듈을 파이썬에서 불러와 사용하려면 어떻게 해야 할까?

먼저 다음과 같이 명령 프롬프트 창을 열고 mod1.py를 저장한 디렉터리(이 책에서는 C:\doit)로 이동한 후 대화형 인터프리터를 실행해 보자.

C:\Users\pahkey>cd C:\doit
C:\doit>dir
...
2014-09-23 오후 01:53 49 mod1.py
...
C:\doit>python
>>> 

반드시 mod1.py 파일을 저장한 C:\doit 디렉터리로 이동한 후 예제를 진행해야 한다. 그래야만 대화형 인터프리터에서 mod1.py 모듈을 읽을 수 있다.

그리고 다음과 같이 따라 해 보자.

>>> import mod1
>>> print(mod1.add(3, 4))
7
>>> print(mod1.sub(4, 2))
2

mod1.py 모듈을 불러오기 위해 import mod1이라고 입력했다. 실수로 import mod1.py라고 입력하지 않도록 주의하자. import는 이미 만들어 놓은 파이썬 모듈을 사용할 수 있게 해 주는 명령어이다. mod1. py 파일에 있는 add 함수를 사용하기 위해서는 mod1.add처럼 모듈 이름 뒤에 도트 연산자(.)를 붙이고 함수 이름을 쓰면 된다.

  • import는 현재 디렉터리에 있는 파일이나 파이썬 라이브러리가 저장된 디렉터리에 있는 모듈만 불러올 수 있다.
  • 파이썬 라이브러리는 파이썬을 설치할 때 자동으로 설치되는 파이썬 모듈을 말한다.

import의 사용 방법은 다음과 같다.

import 모듈_이름

여기에서 모듈 이름은 mod1.py에서 .py 확장자를 제거한 mod1만을 가리킨다.

때로는 mod1.add, mod1.sub처럼 쓰지 않고 add, sub처럼 모듈 이름 없이 함수 이름만 쓰고 싶은 경우도 있을 것이다. 이럴 때는 다음과 같이 사용하면 된다.

from 모듈_이름 import 모듈_함수

위와 같이 함수를 직접 import하면 모듈 이름을 붙이지 않고 바로 해당 모듈의 함수를 쓸 수 있다.

다음과 같이 따라 해 보자.

>>> from mod1 import add
>>> add(3, 4)
7

그런데 이렇게 하면 mod1.py 파일의 add 함수 하나만 사용할 수 있다. add 함수와 sub 함수 둘 다 모듈 이름을 붙이지 않고 사용하려면 어떻게 해야 할까?

2가지 방법이 있다.

from mod1 import add, sub

첫 번째 방법은 위와 같이 from 모듈_이름 import 모듈_함수1, 모듈_함수2처럼 사용하는 것이다. 쉼표(,)로 구분하여 필요한 함수를 불러올 수 있다.

from mod1 import *

두 번째 방법은 * 문자를 사용하는 것이다. 08장에서 공부할 정규 표현식에서 * 문자는 ‘모든 것’이라는 뜻인데, 파이썬에서도 같은 의미로 사용한다. 따라서 from mod1 import *은 mod1 모듈의 모든 함수를 불러와 사용하겠다는 뜻이다.

mod1.py 파일에는 함수가 2개밖에 없으므로 위 2가지 방법은 동일하게 적용된다.

 

 

 

 

 

예외 처리

프로그램을 만들다 보면 수없이 많은 오류를 만나게 된다. 물론 오류가 발생하는 이유는 프로그램이 잘못 동작하는 것을 막기 위한 파이썬의 배려이다. 이번에는 파이썬에서 오류를 처리하는 방법에 대해서 알아보자

오류는 언제 발생하는가?

오류를 처리하는 방법을 공부하기 전에 어떤 상황에서 오류가 발생하는지 한번 알아보자. 오타를 입력했을 때 발생하는 구문 오류 같은 것이 아닌 실제 프로그램에서 자주 발생하는 오류를 중심으로 살펴보자.

먼저 존재하지 않는 파일을 사용하려고 시도했을 때 발생하는 오류이다.

>>> f = open("나없는파일", 'r')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '나없는파일'

위 예에서 볼 수 있듯이 없는 파일을 열려고 시도하면 FileNotFoundError 오류가 발생한다.

이번에는 0으로 다른 숫자를 나누는 경우를 생각해 보자. 이 역시 자주 발생하는 오류이다.

>>> 4 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

4를 0으로 나누려고 하니 ZeroDivisionError 오류가 발생한다.

마지막으로 1가지 예를 더 들어 보자. 다음 오류는 정말 빈번하게 일어난다.

>>> a = [1, 2, 3]
>>> a[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

a[3]은 a의 네 번째 요솟값을 가리키는데, a 리스트에는 값이 3개밖에 없으므로([1, 2, 3]) 값을 얻을 수 없다. 따라서 IndexError 오류가 발생한다. 파이썬은 이런 오류가 발생하면 프로그램을 중단하고 오류 메시지를 보여 준다.

오류 예외 처리 기법

이제 유연한 프로그래밍을 위한 오류 처리 방법에 대해 알아보자.

try-except 문

다음은 오류를 처리하기 위한 try-except 문의 기본 구조이다.

try:
    ...
except [발생오류 [as 오류변수]]:
    ...

try 블록 수행 중 오류가 발생하면 except 블록이 수행된다. 하지만 try 블록에서 오류가 발생하지 않는다면 except 블록은 수행되지 않는다.

except 구문을 자세히 살펴보자.

except [발생오류 [as 오류변수]]:

위 구문을 보면 []를 사용하는데, 이 기호는 괄호 안의 내용을 생략할 수 있다는 관례적인 표기법이다. 즉, except 구문은 다음 3가지 방법으로 사용할 수 있다.

1. try-except만 쓰는 방법

try:
    ...
except:
    ...

이 경우에는 오류의 종류에 상관없이 오류가 발생하면 except 블록을 수행한다.

2. 발생 오류만 포함한 except 문

try:
    ...
except 발생오류:
    ...

이 경우는 오류가 발생했을 때 except 문에 미리 정해 놓은 오류와 동일한 오류일 경우에만 except 블록을 수행한다는 뜻이다.

3. 발생 오류와 오류 변수까지 포함한 except 문

try:
    ...
except 발생오류 as 오류변수:
    ...

이 경우는 두 번째 경우에서 오류의 내용까지 알고 싶을 때 사용하는 방법이다.

이 방법의 예를 들어 보면 다음과 같다.

# try_except.py
try:
    4 / 0
except ZeroDivisionError as e:
    print(e)

위처럼 4를 0으로 나누려고 하면 ZeroDivisionError가 발생하여 except 블록이 실행되고 오류 변수 e에 담기는 오류 메시지를 출력할 수 있다. 출력되는 오류 메시지는 다음과 같다.

division by zero

try-finally 문

try 문에는 finally 절을 사용할 수 있다. finally 절은 try 문 수행 도중 예외 발생 여부에 상관없이 항상 수행된다. 보통 finally 절은 사용한 리소스를 close해야 할 때 많이 사용한다.

다음 예를 살펴보자.

# try_finally.py
try:
    f = open('foo.txt', 'w')
    # 무언가를 수행한다.

    (... 생략 ...)

finally:
    f.close()  # 중간에 오류가 발생하더라도 무조건 실행된다.

foo.txt 파일을 쓰기 모드로 연 후 예외 발생 여부에 상관없이 항상 파일을 닫아 주려면 try-finally 문을 사용하면 된다.

여러 개의 오류 처리하기

try 문 안에서 여러 개의 오류를 처리하려면 다음과 같이 사용해야 한다.

try:
    ...
except 발생오류1:
   ... 
except 발생오류2:
   ...

즉, 0으로 나누는 오류와 인덱싱 오류를 다음과 같이 처리할 수 있다.

# many_error.py
try:
    a = [1,2]
    print(a[3])
    4/0
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")
except IndexError:
    print("인덱싱 할 수 없습니다.")

a는 2개의 요솟값을 가지고 있으므로 a[3]이 IndexError를 발생시켜 "인덱싱할 수 없습니다."라는 문자열을 출력할 것이다. 인덱싱 오류가 먼저 발생했으므로 4 / 0에 따른 Zero DivisionError 오류는 발생하지 않는다.

앞에서 알아본 것과 마찬가지로 오류 메시지도 다음과 같이 확인할 수 있다.

try:
    a = [1,2]
    print(a[3])
    4/0
except ZeroDivisionError as e:
    print(e)
except IndexError as e:
    print(e)

프로그램을 실행하면 ‘list index out of range’라는 오류 메시지가 출력될 것이다.

다음과 같이 ZerroDivisionError와 IndexError를 함께 처리할 수도 있다.

try:
    a = [1,2]
    print(a[3])
    4/0
except (ZeroDivisionError, IndexError) as e:
    print(e)

2개 이상의 오류를 동일하게 처리하기 위해서는 위와 같이 괄호를 사용하여 함께 묶어 처리하면 된다.

try-else 문

try 문에는 다음처럼 else 절을 사용할 수도 있다.

try:
    ...
except [발생오류 [as 오류변수]]:
    ...
else:  # 오류가 없을 경우에만 수행
    ...

try 문 수행 중 오류가 발생하면 except 절, 오류가 발생하지 않으면 else 절이 수행된다.

다음은 try 문에 else 절을 사용한 간단한 예제이다.

# try_else.py
try:
    age=int(input('나이를 입력하세요: '))
except:
    print('입력이 정확하지 않습니다.')
else:
    if age <= 18:
        print('미성년자는 출입금지입니다.')
    else:
        print('환영합니다.')

만약 '나이를 입력하세요: '라는 질문에 숫자가 아닌 다른 값을 입력하면 오류가 발생하여 '입력이 정확하지 않습니다.'라는 문장을 출력한다. 오류가 없을 경우에만 else 절이 수행된다.

오류 회피하기

코드를 작성하다 보면 특정 오류가 발생할 경우 그냥 통과시켜야 할 때가 있다. 다음 예를 살펴보자.

# error_pass.py
try:
    f = open("나없는파일", 'r')
except FileNotFoundError:
    pass

try 문 안에서 FileNotFoundError가 발생할 경우, pass를 사용하여 오류를 그냥 회피하도록 작성한 예제이다.

오류 일부러 발생시키기

이상하게 들리겠지만, 프로그래밍을 하다 보면 종종 오류를 일부러 발생시켜야 할 경우도 생긴다. 파이썬은 raise 명령어를 사용해 오류를 강제로 발생시킬 수 있다.

예를 들어 Bird 클래스를 상속받는 자식 클래스는 반드시 fly라는 함수를 구현하도록 만들고 싶은 경우(강제로 그렇게 하고 싶은 경우)가 있을 수 있다. 다음 예를 살펴보자.

# error_raise.py
class Bird:
    def fly(self):
        raise NotImplementedError

Bird 클래스를 상속받는 자식 클래스는 반드시 fly 함수를 구현해야 한다는 의지를 보여 준다. 만약 자식 클래스가 fly 함수를 구현하지 않은 상태로 fly 함수를 호출한다면 어떻게 될까?

NotImplementedError는 파이썬에 이미 정의되어 있는 오류로, 꼭 작성해야 하는 부분이 구현되지 않았을 경우 일부러 오류를 발생시키기 위해 사용한다.

class Eagle(Bird):
    pass

eagle = Eagle()
eagle.fly()

Eagle 클래스는 Bird 클래스를 상속받았다. 그런데 Eagle 클래스는 fly 메서드를 오버라이딩하여 구현하지 않았다. 따라서 eagle 객체의 fly 메서드를 수행하는 순간 Bird 클래스의 fly 메서드가 수행되어 NotImplementedError가 발생한다.

Traceback (most recent call last):
  File "...", line 33, in <module>
    eagle.fly()
  File "...", line 26, in fly
    raise NotImplementedError
NotImplementedError

상속받는 클래스에서 메서드를 재구현하는 것을 ‘메서드 오버라이딩’이라고 한다.

NotImplementedError가 발생하지 않게 하려면 다음과 같이 Eagle 클래스에 fly 함수를 구현해야 한다.

class Eagle(Bird):
    def fly(self):
        print("very fast")

eagle = Eagle()
eagle.fly()

위 예처럼 fly 함수를 구현한 후 프로그램을 실행하면 오류 없이 다음 문장이 출력된다.

very fast

예외 만들기

프로그램을 수행하다가 특수한 경우에만 예외 처리를 하려고 종종 예외를 만들어서 사용한다. 이번에는 직접 예외를 만들어 보자.

예외는 다음과 같이 파이썬 내장 클래스인 Exception 클래스를 상속하여 만들 수 있다.

# error_make.py
class MyError(Exception):
    pass

그리고 별명을 출력하는 함수를 다음과 같이 작성해 보자.

def say_nick(nick):
    if nick == '바보':
        raise MyError()
    print(nick)

그리고 다음과 같이 say_nick 함수를 호출해 보자.

say_nick("천사")
say_nick("바보")

저장한 후 프로그램을 실행해 보면 다음과 같이 "천사"가 한 번 출력된 후 MyError가 발생한다.

천사
Traceback (most recent call last):
  File "...", line 11, in <module>
    say_nick("바보")
  File "...", line 7, in say_nick
    raise MyError()
__main__.MyError

이번에는 예외 처리 기법을 사용하여 MyError 발생을 예외 처리해 보자.

try:
    say_nick("천사")
    say_nick("바보")
except MyError:
    print("허용되지 않는 별명입니다.")

프로그램을 실행하면 다음과 같이 출력된다.

천사
허용되지 않는 별명입니다.

만약 오류 메시지를 사용하고 싶다면 다음처럼 예외 처리를 하면 된다.

try:
    say_nick("천사")
    say_nick("바보")
except MyError as e:
    print(e)

하지만 프로그램을 실행해 보면 print(e)로 오류 메시지가 출력되지 않는 것을 확인할 수 있다. 오류 메시지를 출력했을 때 오류 메시지가 보이게 하려면 오류 클래스에 다음과 같은 __str__ 메서드를 구현해야 한다. __str__ 메서드는 print(e)처럼 오류 메시지를 print 문으로 출력할 경우에 호출되는 메서드이다.

class MyError(Exception):
    def __str__(self):
        return "허용되지 않는 별명입니다."

프로그램을 다시 실행해 보면 "허용되지 않는 별명입니다."라는 오류 메시지가 출력되는 것을 확인할 수 있을 것이다.