3장. 연산자, 식, 그리고 프로그램 흐름

이 장에서는 코드를 평가하고, 의미있는 조건부 논리의 단락을 작성하는 각각의 방법을 깊이 있게 살펴보려고 한다. 파이썬의 식에서 사용할 수 있는 많은 연산자들을 자세히 다룰 것이다. 되풀이 구조라든지 기본적인 프로그램 흐름 같이 이미 논의한 적이 있는 주제에 대해서도 좀 더 다루어볼 것이다.

식에 대하여 자세히 논의하는 것에서부터 시작하겠다. 1장에서 다루었던 바와 같이, 식이란 값을 얻어내기 위해 평가를 수행하는 코드 조각을 말한다. 앞장을 통해서 이미 몇가지 식을 살펴보았다. 이 장에서는, 식을 생성하는 연산자에 대한 좀 더 내부적인 부분과, 사용 가능한 서로 다른 종류의 식에 초첨을 맞추도록 하겠다. 이 장에서는 되풀이와 조건식을 위한 코드 단락을 어떻게 정의하는지 좀 더 자세히 살펴보도록 하겠다.

이 장에서는 수식과 논리식을 작성하는 방법도 자세히 알아보도록 하겠다. 끝으로, 거듭 할당을 통하여 어떻게 하면 둘 이상의 연산자를 하나로 묶을 수 있는지 논의하겠다.

식의 종류

파이썬에서 식expression이란 결과 또는 값을 만들어내는 코드 조각을 말한다. 식이라고 하면 코드에서 수학 연산을 수행하기 위한 것을 떠올리게 된다. 하지만, 다른 목적을 위해 쓰이는 수많은 식들이 있다. 2장에서, 문자열 조작, 순서 및 사전 연산, 그리고 집합을 다루는 방법을 다루었다. 이러한 개체들에 대하여 수행하는 모든 연산이 파이썬에서의 식을 이룬다. 식에 대한 다른 예제로 메소드 및 함수 호출, 그리고 목록 썰기slicing와 색인 작업 등이 있다.

수학 연산

파이썬은 기본적인 수학 연산을 모두 제공한다. 이 절에서는 각 연산자가 어떻게 쓰이는지 간략히 살펴보도록 하겠다. 수식을 사용하는데 도움이 되는 내장 함수에 대해서도 배우게 될 것이다.

이것이 처음으로 배우는 프로그래밍 언어가 아니라면, 프로그램 내에서 수학 연산을 수행하는 것을 잘 알고 있으리라 믿어 의심치 않는다. 파이썬은 수학에 있어서 다른 대부분의 프로그래밍 언어와 별반 다르지 않으며, 직관적으로 수학적인 계산을 수행하고 수식을 다룰 수 있다. 표 3-1에 수학 연산자를 나열하였다.

표 3-1. 수학 연산자

연산자 설명
+ 덧셈
- 뺄셈
* 곱셈
/ 나눗셈
// 나눗셈의 몫
% 모듈로 (나눗셈의 나머지)
** 지수 연산자
+var 단항 덧셈
-var 단항 뺄셈

표 3-1에 나와있는 대부분의 연산자는 아래 예제에서와 같이 여러분이 생각하는 대로 동작할 것이다.

예제 3-1. 수학 연산자

>>> # 기본적인 수학 계산을 수행
>>> 10 - 6
4
>>> 9 * 7
63

하지만, 나눗셈, 버리는 나눗셈, 모듈로, 거듭제곱, 그리고 단항 연산자 같은 것들에 대해서는 약간의 설명이 필요하다. 버리는 나눗셈은 나눗셈의 결과에서 자동적으로 소수점 이하를 버리고 정수를 남기며, 모듈로*modulo*는 버리는 나눗셈 연산의 나머지를 반환한다. 거듭제곱 연산자는 예상할 수 있듯이, 연산자 왼쪽에 있는 숫자를 n번 곱한 결과를 반환하며, 연산자 오른쪽의 n은 숫자를 나타낸다.

예제 3-2. 나머지와 거듭제곱

>>> 36 // 5
7

>>> # 나머지를 반환하는 모듈로
>>> 36 % 5
1

>>> # 5의 제곱
>>> 5**2
25

>>> # 100의 제곱
>>> 100**2
10000

현재 구현되어 있는 나눗셈은 특정 상황에서 논쟁을 일으키는 흥미로운 주제이다. 문제 10/5 = 2는 확실히 참이다. 그러나, 현재의 구현에서는, 나눗셈은 때때로 예기치 않은 결과가 도출되게끔 반올림을 한다. 자이썬 2.5에서는 __future__에서 새로운 나눗셈을 들여올 수 있게 되었다. 2.5나 그 이전 판의 표준 나눗셈에서, 인자가 int 혹은 long인 경우에 반환되는 몫은 소수점 이하를 버린 값이다. 하지만, 인자가 부동소수점 혹은 복소수인 경우에는 납득할만한 근사값이 반환된다. 종종 이러한 해법의 결과는 기대하였던 납득할 만한 근사치도 아니고 실제 나눗셈도 아니다. __future__로부터 division을 들여오면 / 연산자를 사용할 때 실제 나눗셈의 값이 반환되도록 대체하며, 부동소수의 나눗셈은 // 연산자를 사용할 때만 일어나게 된다. 이전 버전과의 호환성을 해치지 않도록 하기 위하여, 개발자는 모듈 __future__로 알려진 수리 부문 구현을 배치하였다. __future__ 모듈은 실제로 어떤 미래의 개정에 표준 언어의 일부로 포함시킬 의도가 코드가 포함되어 있다. 새로운 복구된 버전을 사용하기 위해서는, 나눗셈을 수행하기 전에 항상 __future__를 들여오는 것이 중요하다. 다음의 코드 조각을 보라.

예제 3-3. 나눗셈의 반올림 문제

>>> # 예상대로 동작함
>>> 14/2
7
>>> 10/5
2
>>> 27/3
9

>>> # 이제 소수가 발생하도록 숫자를 나누어보자
>>> # 여기서는 1.5가 나와야 한다
>>> 3/2
1

>>> # 다음은 1.4가 나와야 한다
>>> 7/5
1

>>> # 다음의 경우에는, 2.3333을 기대한다
>>> 14/6
2

위에서 보듯이, 우리가 기대했던 것은 십진수인데 실제로는 정수 값을 받았다. 애초에 나눗셈을 구현했던 개발자들은 이러한 문제점을 인정하고 새로운 __future__ 구현을 사용하여 개선하기로 하였다.

예제 3-4. __future__ 나눗셈으로 작업하기

>>> # 우선 __future__로부터 division을 가져온다
from __future__ import division

>>> # 그런 다음 평소와 같이 나눗셈을 수행하면 예상했던 결과를 얻을 수 있다
>>> 14/2
7.0
>>> 10/5
2.0
>>> 27/3
9.0
>>> 3/2
1.5
>>> 7/5
1.4
>>> 14/6
2.3333333333333335

자바에서 추가적으로 반올림으로 처리하는 부분으로 인해 자이썬 구현과 CPython이 서로 다른 점이 있다는 것에 주의해야 한다. 자이썬과 CPython 양쪽 다 동일한 IEEE 부동소수점을 사용하므로 반올림이 서로 다르게 보이는 것 뿐이다. 각각의 경우를 살펴보자.

예제 3-5. 자이썬과 CPython 나눗셈의 미묘한 차이점

>>> # CPython 2.5의 반올림
>>> 5.1/1
5.0999999999999996

>>> # 자이썬 2.5
>>> 5.1/1
5.1

단항 연산자는 양수나 음수를 평가하는데 사용할 수 있다. 단항 양(+) 연산자는 숫자를 양수 1로 곱하며(아무 변화도 일으키지 않는다), 반항 음(-) 연산자는 숫자를 음수 1로 곱한다.

예제 3-6. 단항 연산자

>>> # 단항 뺄셈
>>> -10 + 5
-5
>>> +5 - 5
0
>>> -(1 + 2)
-3

이 장의 첫 부분에 명시한 바와 같이, 여러 내장 수학 함수가 널려있다. 표 3-2에 내장 수학 함수를 나열하였다.

표 3-2. 수학 내장 함수

기능 설명
abs(var) 절대값
pow(x, y) ** 연산자 대신에 사용 가능
pow(x,y,modulo) 삼항 지수-나머지 (x ** y) % modulo
round(var[, n]) 10-n 또는 (10**-n)의 반올림한 근사치를 반환. n의 기본값은 0
divmod(x, y) 나눗셈의 몫과 나머지로 이루어진 튜플을 반환

예제 3-7. 수학 내장 함수

>>> # 다음 코드는 수학 내장 함수를 사용하는 몇 가지 예제를 제공한다
>>> # 9의 절대값
>>> abs(9)
9

>>> # -9의 절대값
>>> abs(-9)
9

>>> # 8을 4로 나눈 몫과 나머지로 이루어진 튜플
>>> divmod(8,4)
(2, 0)

>>> # 똑같은 일을 하지만, 이번에는 나머지(모듈로)가 반환됨
>>> divmod(8,3)
(2, 2)

>>> # 8의 제곱을 구함
>>> pow(8,2)
64

>>> # 8의 제곱을 3으로 나눈 나머지  ((8 **2) % 3)
>>> pow(8,2,3)
1

>>> # 반올림을 수행
>>> round(5.67,1)
5.7
>>> round(5.67)
6.00

비교 연산자

비교 연산자는 두 개 이상의 식 또는 변수의 비교를 위해 사용할 수 있다. 위에서 설명한 수학 연산자와 마찬가지로, 자바의 비교 연산자와 큰 차이는 없다. 표 3-3 참조.

표 3-3. 비교 연산자

연산자 설명
>
< 작음
>= 크거나 같음
<= 작거나 같음
!= 같지 않음
== 같음

예제 3-8. 비교 연산자의 예

>>> # 단순 비교
>>> 8 > 10
False
>>> 256 < 725
True
>>> 10 == 10
True

>>> # 식 내에서 비교
>>> x = 2*8
>>> y = 2
>>> while x != y:
...     print 'Doing some work...'
...     y = y + 2
...
Doing some work...
Doing some work...
Doing some work...
Doing some work...
Doing some work...
Doing some work...
Doing some work...

>>> # 삼항 비교
>>> 3<2<3
False
>>> 3<4<8
True

비트 연산자

파이썬의 비트 연산자는 숫자를 2의 보수로서 다루는 연산자의 집합이다. 비트 연산자를 사용할 때는, 숫자를 0과 1로 구성된 문자열로 취급한다는 말이다. 2의 보수에 대해 개념을 잡지 못하고 있다면, 위키피디아에서 다음의 문서를 살펴보는 것이 도움이 될 것이다. (http://ko.wikipedia.org/wiki/2의_보수) 비트 연산자는 정수와 긴 정수에만 사용할 수 있다는 것에 유념하라. 표 3-4에서 비트 연산자들 간의 차이를 살펴본 다음에 몇 가지 예제도 살펴보자.

표 3-4. 비트 연산자

연산자 설명
& 논리곱 연산자로서 비트가 두 항에 모두 나타나는 경우 비트를 결과에 복사함
|| 논리합 연산자로서 비트가 두 항 중 어느 곳에 나타나는 경우 비트를 결과에 복사함
^ 배타적 논리합 연산자로서 어느 한 쪽의 항에만 비트가 존재할 경우 비트를 결과에 복사함
~ 부정 연산자로서 비트를 뒤집어서 각 비트에 대하여 정확히 반대를 반환함

이진 형식의 숫자 두 개에 대해서 비트 연산자를 써서 작업을 하고 싶다고 하자. 주어진 숫자는 14과 27이다. 14를 이진 형식(2의 보수)로 나타내면 00001110, 그리고 27은 00011011이다. 비트 연산자는 이진 형식으로 나타낸 숫자의 1과 0 하나하나에 대하여 연산을 수행하고 그 결과를 반환한다. 파이썬은 비트를 반환하지는 않고, 결과 비트에 대한 정수 값을 반환한다. 다음의 예제에서 14와 27을 가지고 비트 연산자로 작업을 해보도록 하겠다.

예제 3-9. 비트 연산자

>>> 14 & 27
10
>>> 14 | 27
31
>>> 14 ^ 27
21
>>> ~14
-15
>>> ~27
-28

위의 예제에 나타난 각각의 숫자에 대한 이진 표현을 살펴보도록 하자.

14 & 27 = 00001110 and 00011011 = 00001010 (정수 10)
14 | 27 = 00001110 or 000110011 = 00011111 (정수 31)
14 ^ 27 = 00001110 xor 000110011 = 00010101 (정수 21)
~14 = 00001110 = 11110001 (정수 -15)

이동 연산자도 숫자의 이진 비트 표현에 대하여 작업한다는 점에서 비슷하다(표 3-5를 보라). 왼쪽 이동 연산자는 왼쪽 항의 값을 오른쪽 항에서 지정한 비트만큼 왼쪽으로 이동시킨다. 오른쪽 이동 연산자는 정확히 반대로 동작하여, 왼쪽 항의 값을 오른쪽 항에서 지정한 비트만큼 오른쪽으로 이동시킨다. 본질적으로 왼쪽 이동 연산자는 왼쪽에 있는 항에다가 오른쪽 항에서 지정한 횟수만큼 숫자 2를 곱하는 셈이다. 반대로 오른쪽 이동 연산자는 왼쪽에 있는 항을 오른쪽 항에서 지정한 횟수만큼 2로 나누는 것이다.

표 3-5. 이동 연산자

x<<n 왼쪽으로 이동 (숫자 x에 2를 n번 곱한 것과 동등함)
x>>n 오른쪽으로 이동 (숫자 x를 2로 n번 나눈 것과 동등함)

보다 구체적으로는, 이동 연산자의 오른쪽에 있는 숫자를 n이라고 할 때, 왼쪽 이동 연산자(<<)는 숫자에 2를 n번 곱한다. 오른쪽 이동 연산자는, 이동 연산자의 오른쪽에 있는 숫자를 n이라 할 때, 숫자를 2로 n번 나누는 것이다. 이러한 연산자들의 수행 결과는 __future__ 나눗셈을 들여오는 것에 영향을 받지 않는다.

예제 3-10. 이동 연산자

# 왼쪽으로 이동, 여기서는 3*2
>>> 3<<1
6
# 3*2*2와 동등함
>>> 3<<2
12
# 3*2*2*2*2*2와 동등함
>>> 3<<5
96
# 오른쪽으로 이동
# 3/2와 동등함
>>> 3>>1
1
# 9/2와 동등함
>>> 9>>1
4
# 10/2와 동등함
>>> 10>>1
5
# 10/2/2와 동등함
>>> 10>>2
2

비트 연산자는 그리 자주 쓰이는 것은 아니지만 익혀두면 유용하다. 수학적인 상황에서 작업할 때에는 특히 중요하다.

복합 대입 연산자

복합 대입 연산자는 연산과 할당을 합쳐놓은 것이다(표 3-6을 보라). 변수가 이전에 가졌던 값을 수정하여 할당하는 일 같은 것에 쓸 수 있다. 복합 대입 연산자는 코드를 간결하게 만드는데에 도움이 될 수도 있지만, 너무 많이 쓰다보면 코드를 읽기가 어려워질 수도 있다.

예제 3-11. 복합 대입 연산자

>>> x = 5
>>> x
5
# x의 값에 1을 더하여 그 값을 x에 할당
>>> x+=1
>>> x
6
# x의 값에 5를 곱하여 그 값을 x에 할당
>>> x*=5
>>> x
30

표 3-6. 복합 대입 연산자

연산자 동치
a += b a = a + b
a -= b a = a - b
a *= b a = a * b
a /= b a = a / b
a %= b a = a % b
a //= b a = a // b
a **= b a = a** b
a &= b a = a & b
a |= b a = a | b
a ^= b a = a ^ b
a >>= b a = a >> b
a <<= b a = a << b

부울 식

둘 혹은 그 이상의 값이나 식을 평가하는 데에 있어서는 다른 언어에 있는 것과 유사한 구문을 사용하며 그 논리 또한 거의 비슷하다. 파이썬에서의 참과 거짓은 자바 언어의 상수와 거의 비슷하다. 참은 실제로는 숫자 1을, 그리고 거짓은 숫자 0을 나타낸다. 부울 값을 표현하기 위해 0과 1을 써서도 쉽게 작성할 수는 있겠지만, 가독성과 유지보수를 위해서 True와 False “상수”를 쓰는 것이 좋다. 자바 개발자 여러분은 이 두 단어의 첫 글자를 대문자로 쓰기 바란다. 그렇지 않으면 NameError를 보게 될 것이다.

부울 속성은 정수와 부울 값에 대해서 작업하는데에 국한되는 것이 아니라, 다른 값과 객체와도 작동한다. 예를 들어 비어 있지 않은 개체를 단순히 부울 식에 전달하기만 하면 부울 내용에서는 참으로 평가하게 된다. 문자열이 어떤 것이든지 담고 있는지의 여부를 알아보는 좋은 방법이다. 표 3-7을 보라.

예제 3-12. 문자열 검사

>>> mystr = ''
>>> if mystr:
...     'Now I contain the following: %s' % (mystr)
... else:
...     'I do not contain anything'
...
'I do not contain anything'
>>> mystr = 'Now I have a value'
>>> if mystr:
...     'Now I contain the following: %s' % (mystr)
... else:
...     'I do not contain anything'
...
'Now I contain the following: Now I have a value'

표 3-7. 부울 조건

조건부 논리
and x and y 평가에 있어서, x가 거짓으로 판명되면 그것을 반환하고, 그렇지 않은 경우에는 y를 평가하여 결과값을 반환
or x or y 평가에 있어서, x가 참으로 판명되면 그것을 반환하고, 그렇지 않은 경우에는 y를 평가하여 결과값을 반환
not not x 평가에 있어서, x가 거짓이라는 것은 x의 반대를 의미함

다른 프로그래밍 언어들과 마찬가지로, 어느 연산자를 먼저 평가할 것인지에 대해서 정해진 순서가 있다. 예를 들어, a + b * c라는 식이 있다고 하면, 어느 연산을 가장 먼저 처리하여야 하는지의 순서가 있다. 파이썬의 연산 순서는 표 3-8에 있으며, 높은 순위일수록 위쪽에, 낮은 순위일수록 아래쪽에 나타내었다. 동일한 연산자가 연달아 있을 경우 왼쪽으로부터 오른쪽으로 묶이나, 제곱(**) 연산자는 예외이다.

표 3-8. 파이썬의 연산 순서

가장 높은 것부터 가장 낮은 것까지의 연산자의 우선순위 이름
+var, -var, ~var 단항 연산자
** 제곱 연산자
*, /, //, % 곱셈, 나눗셈, 몫, 나머지
+, - 덧셈, 뺄셈
<<, >> 왼쪽 및 오른쪽 이동
& 논리곱
^ 배타적 논리합
|| 논리합
<, >, <=. >= , <> 비교 연산자
==, != , is, is not, in, not in 동등 및 포함
and, or, not 여부 판단 조건

부울 조건을 가지고 작업할 때 중요한 것은, ‘and’와 ‘or’는 왼쪽으로부터 오른쪽으로 묶인다는 점이다. 몇 가지 예를 살펴보자.

예제 3-13. 연산 순서 예제

>>> # 몇 가지 변수를 정의
>>> x = 10
>>> y = 12
>>> z = 14

>>> # (y*z)를 먼저 평가한 다음, x를 더함
>>> x + y * z
178

>>> # (x * y)를 먼저 평가한 다음, 그 결과에서 z를 뺌
>>> x * y - z
106

>>> # 연이은 비교는, 논리적인 'and'를 암시함

>>> # 이 경우, x < y and y <= z and z > x
>>> x < y <= z > x
True

>>> # (2 * 0)이 먼저 평가되며 그 결과는 False 또는 0이므로, 그것이 반환됨
>>> 2 * 0 and 5 + 1
0

>>> # (2 * 1)이 먼저 평가되며 그 결과는 True이거나 0 아닌 값이므로, (5 + 1)이 평가되어 반환됨
>>> 2 * 1 and 5 + 1
6

>>> # 만약 x가 참인 경우 x를 반환하며, 그렇지 않은 경우 y가 거짓이면 y를 반환.
>>> # 둘 중 어느 것도 일어나지 않는 경우에는 z를 반환.
>>> x or (y and z)
10

>>> # 이 예제에서는 'and' 'or' 논리로 인하여 (7 - 2)를 평가하여 반환
>>> 2 * 0 or ((6 + 8) and (7 - 2))
5

>>> # 이 경우는, 제곱 연산이 먼저 평가되고, 그 다음이 덧셈임
>>> 2 ** 2 + 8
12

형 변환

자이썬에는 한 자료형을 다른 것으로 변환해주는 형 변환 함수들이 내장되어 있다(표 3-9 참조). 사실 자이썬의 모든 자료형은 클래스 개체이므로, 형 변환 함수들은 실제로는 한 클래스 유형을 다른 것으로 변환해준다. 대부분의 경우, 그러한 내장함수들은 변환하고자 하는 형에 따른 이름을 갖고 있기 때문에 기억하기 쉽다.

표 3-9. 형 변환 함수

함수 설명
chr(value) 정수를 문자로 변환
complex(real [,imag]) 복소수를 만듦
dict(sequence) 주어진 (키, 값) 튜플의 순서로부터 사전을 만듦
eval(string) 문자열을 평가하여 수학적 계산에 유용한 개체를 반환. 주의: 이 함수는 올바로 사용하지 않을 경우 보안 위험을 초래할 수 있으므로 매우 조심하여야 함
float(value) 숫자를 부동소수점수로 변환
frozenset(set) 집합을 동결집합으로 변환
hex(value) 정수로부터 16진수를 나타내는 문자열로 변환
int(value [, base]) 정수로 변환하며 base 문자열이 주어진 경우에는 사용함
list(sequence) 주어진 순서를 목록으로 변환
long(value [, base]) long(긴 정수)형으로 변환하며 base 문자열이 주어진 경우에는 사용함
oct(value) 정수로부터 8진수를 나타내는 문자열로 변환
ord(value) 문자를 가리키는 정수값으로 변환
repr(value) 개체를 표현 문자열로 변환. 표현식을 역따옴표로 둘러싼 것과 같음(`x + y`). 개체에 대하여 출력가능하며 평가 가능한 문자열을 반환
set(sequence) sequence를 집합으로 변환
str(value) 개체를 문자열로 변환. value에 대하여 출력가능한 문자열을 반환 하나, 평가할 수는 없음
tuple(sequence) 주어진 sequence를 튜플로 변환
unichr(value) 정수를 유니코드 문자로 변환

예제 3-14. 변환 함수의 예

>>> # 정수가 나타내는 문자를 반환
>>> chr(4)
'\x04'
>>> chr(10)
'\n'

>>> # 정수를 부동소수점수로 변환
>>> float(8)
8.0

>>> # 문자를 나타내는 정수 값으로 변환
>>> ord('A')
65
>>> ord('C')
67
>>> ord('z')
122

>>> # 임의의 개체에 대하여 repr()을 사용
>>> repr(3.14)
'3.14'
>>> x = 40 * 5
>>> y = 2**8
>>> repr((x, y, ('one','two','three')))
"(200, 256, ('one', 'two', 'three'))"

다음은 eval() 함수의 사용법을 이해하도록 돕기 위한 예제이다. eval() 함수를 잘못 사용할 경우 위험이 따르며 보안상의 위협을 야기할 수 있다는 점을 다시 한 번 밝혀둔다. 사용자로부터 받아들인 텍스트를 가지고 eval()을 수행하려면, 사용자가 입력한 문자열이 보안을 위배하지 않는다는 것을 확신할 수 있도록 표준적인 보안 조치를 취해야 한다.

예제 3-15. eval()의 예

>>> # 키보드 입력이 문자열로 쓰여진 식(x * y)이라고 하자
>>> x = 5
>>> y = 12
>>> keyboardInput = 'x * y'

>>> # 키보드를 통해 입력하는 문자열이 평가를 하기에 안전한지 확인하기 위해 보안 검사를 할 필요가 있다.
>>> # 이러한 작업은 이 장의 주제를 벗어나기는 하지만, 평가하기에 앞서 악성 코드의 가능성에 대비하기 위해 키보드 입력을 비교해보는 것이 바람직하다.
>>> eval(keyboardInput)
60

식을 사용한 프로그램 흐름 제어

이 책의 앞부분에서 배운 것과 같이, 파이썬의 구문은 공백, 정렬, 기법에 대해 주의깊게 작성한다. 각각의 코드는 각각의 제어 구조를 다른 것들로부터 구분할 수 있도록 일관적으로 들여쓰기되어야한다. 파이썬 구문의 일관적인 공백이 주는 가장 큰 이점은 중괄호 {} 같은 구분자를 멀리할 수 있다는 것이다. 예를 들면, 자바에서 되풀이를 작성하려면 시작과 끝에 중괄호를 반드시 써야 한다. 파이썬에서는 단순히 공백만 있으면 괄호의 역할을 제대로 해낸다.. 전체 프로그램에 있어서 들여쓰기는 네 칸 씩 하는 것이 관례적이기도 하고 좋은 방법이다. 관습에 대해서는 PEP 8, 파이썬 코드 스타일 안내(www.python.org/dev/peps/pep-0008/)를 보라. 제어 흐름에 대하여 이 관습을 따르면 유지보수가 쉬운 소프트웨어를 개발할 수 있다.

if-elif-else 문

파이썬의 표준적인 if-elif-else 조건문은 식을 평가하고 그 결과에 따라 프로그램 논리를 분기하기 위해 사용한다. if-elif-else 문은 우리가 앞서 논의하였던 어떠한 식으로든 구성할 수 있다. 목표는 평가를 통해 참이나 거짓을 만들어내는 비교 식을 작성하는 것이다. 1장에서 본 것과 같이, if-elif-else 문의 논리는 식의 평가결과가 참일 경우에 한 쪽을 따라가게 되며, 평가결과가 거짓일 경우 다른 쪽을 따라가게 된다.

if-else 식은 필요한 만큼 엮을 수도 있다. if-else를 조합하는 열쇠말은 elif로서, 모든 조건문의 처음과 마지막 식 사이에 사용된다.

elif 부분은 프로그램 논리의 가독성을 보장하는데 도움이 된다. if 문을 너무 많이 중첩시키게 되면 프로그램을 유지보수하기 어려워질 수 있다. 처음의 if 식을 평가하였을 때, 그 결과가 거짓이면, 다음의 elif 식이 평가되며, 그 결과가 거짓이면 또 다음으로 이어진다. if나 elif 식의 어느 하나라도 참으로 평가되면 그 부분의 if 구문을 처리하게 된다. 모든 식이 거짓이 되어버리는 경우라면 마지막의 else 식이 평가된다.

다음의 예제들은 표준 if-elif-else 문을 사용하는 몇 가지 방법을 보여준다. if-elif-else 구조에서는 어떤 식이든지 평가할 수 있다는 점에 유의하기 바란다. 여기서는 간단한 예를 들었지만, 필요에 따라서 식 내부의 논리를 복잡하게 쓸 수 있다.

예제 3-16. 표준적인 if-elif-else

# terminal symbols are left out of this example so that you can see the precise indentation
pi =3.14
x = 2.7 * 1.45
if x == pi:
    print 'The number is pi'
elif x > pi:
    print 'The number is greater than pi'
else:
    print 'The number is less than pi'

빈 목록이나 문자열은 거짓으로 평가되어, if-elif-else 문에서 비교하기에 용이하다.

예제 3-17. 빈 목록을 평가

>>> # 목록이 비어있는지 검사하기 위하여 if 문을 사용
>>> # mylist는 이름의 목록이 될 것이라고 가정
>>> mylist = []
>>> if mylist:
...     for person in mylist:
...         print person
... else:
...     print 'The list is empty'
...
The list is empty

while 되풀이

우리가 1장에서 건드려보았던 또 다른 구조는 되풀이loop이다. 모든 프로그래밍 언어가 되풀이를 구현하고 있으며, 파이썬도 다를 바가 없다. 요점을 되풀이하자면, 파이썬 언어는 while과 for 되풀이로 알려진 두 가지 주요한 되풀이를 제공한다.

while 되풀이 논리는 자바의 while 되풀이와 동일한 의미를 따른다. while 되풀이는 주어진 식을 평가하여 그 결과가 더 이상 참이 아닌 거짓을 갖게 될 때까지 되풀이를 계속한다. 대부분의 while 되풀이는 x <= y와 같은 비교 표현을 가지며, 이 경우는 x가 y보다 커질 경우에 거짓으로 평가된다. 되풀이는 식이 거짓으로 평가될 때까지 처리를 계속하게 된다. 이때 되풀이가 끝나며 자바의 구현도 그러하다. 한편 파이썬에서는 되풀이가 완료되었을 때 수행하도록 else 구문도 쓸 수 있다.

예제 3-18. 파이썬 while 문

>>> x = 0
>>> y = 10
>>> while x <= y:
...     print 'The current value of x is: %d' % (x)
...     x += 1
... else:
...     print 'Processing Complete...'
...
The current value of x is: 0
The current value of x is: 1
The current value of x is: 2
The current value of x is: 3
The current value of x is: 4
The current value of x is: 5
The current value of x is: 6
The current value of x is: 7
The current value of x is: 8
The current value of x is: 9
The current value of x is: 10
Processing Complete...

이러한 else 문은 위와 같이 while 문에서 집중적인 처리를 수행하는 경우 그러한 작업의 완료를 사용자에게 알려줄 수 있어 유용하다. 또한 코드를 디버깅할 때나, 되풀이를 마치고 정리가 필요할 때에도 쓸모가 있다.

예제 3-19. else를 사용하여 계수기를 재설정

>>> total = 0
>>> x = 0
>>> y = 20
>>> while x <= y:
...     total += x
...     x += 1
... else:
...     print total
...     total = 0
...
210

continue 문

continue 문은 되풀이 수행 중에 현재 되풀이의 뒷 부분을 건너뛰도록 파이썬에게 알려주고자 할 때 사용한다. 파이썬 인터프리터가 continue 문을 보게 되면, 그것은 루프의 현재 반복을 종료하고 다음 반복문 처리를 계속한다. continue 문은 for나 while 되풀이 어느 쪽에서나 사용할 수 있다.

예제 3-20. continue 문

# 범위에 대하여 반복하되 짝수만 출력
>>> x = 0
>>> while x < 10:
...     x += 1
...     if x % 2 != 0:
...         continue
...     print x
...
2
4
6
8
10

이 예제에서는 x가 홀수일 때마다 ‘continue’ 문이 실행되어 되풀이의 다음 차례로 이동한다. x가 짝수일 때에는 출력한다.

break 문

continue 문과 비슷하게, break 문을 되풀이 내에서 사용할 수 있다. break 문은 되풀이를 완전히 끝내고 프로그램이 그 다음 작업으로 넘어가도록 할 때 사용한다. 현재의 반복만 끝내고 다음 반복으로 진행하는 continue와는 다르다. 한번 확인해 보자.

예제 3-21. break 문

>>> x = 10
>>> while True:
...     if x == 0:
...         print 'x is now equal to zero!'
...         break
...     if x % 2 == 0:
...         print x
...     x -= 1
...
10
8
6
4
2
x is now equal to zero!

앞의 예제에서 되풀이의 종료 조건은 항상 참이기 때문에, break를 만날 때에만 되풀이의 실행이 끝나게 된다. 다른 되풀이 안에 되풀이가 포함되어 있을 때(중첩 되풀이 구조)의 break 문은 안쪽의 되풀이만 끝마친다.

for 되풀이

되풀이는 어떠한 반복 가능한 개체에 대하여든 사용할 수 있다. 차례가 돌아오면 각각의 개체에 대하여 어떤 처리를 수행한다. break와 continue 문 양쪽 다 되풀이 내에서 사용할 수 있다. 파이썬의 for 문은 else 문도 갖추고 있다는 점에서 자바의 for 문과 차이가 있다. 다시 말해, else 문은 어떠한 방해나 예외 제기 없이 반복 처리가 완료되었을 때에 실행된다. 또한, Java 5 이전의 for 되풀이에 익숙하다면 파이썬의 구문을 사랑하게 될 것이다. Java 5에 들어서, for 문의 구조는 파이썬처럼 보다 쉽게 조정되었다.

예제 3-22. 자바와 파이썬의 for 되풀이 비교

Example of Java for-loop (pre Java 5)

for(x = 0; x <= myList.size(); x++){
// processing statements iterating through myList
System.out.println("The current index is: " + x);
}

예제 3-23. 파이썬의 for 되풀이 예제

my_list = [1,2,3,4,5]
>>> for value in my_list:
# processing statements using value as the current item in my_list
...     print 'The current value is %s' % (value)
...
The current value is 1
The current value is 2
The current value is 3
The current value is 4
The current value is 5

보는 것과 같이, 파이썬 구문은 이해하기 쉽지만, 지금으로서는 키 입력을 그다지 줄여주지는 못한다. 여전히 우리는 되풀이가 반복될 때마다 색인(여기서는 x)을 증가시킴으로서 관리해주어야 한다. 그렇지만 파이썬은 자바에서와 같이 되풀이에서 키 입력을 적게 할 수 있도록 색인을 자동으로 증가시켜주는 함수를 내장하고 있다. 바로 enumerate(순서형) 함수이다. 색인을 자동으로 관리해주는 것이다.

예제 3-24. Enumerate() 함수

>>> myList = ['jython','java','python','jruby','groovy']
>>> for index, value in enumerate(myList):
...     print index, value
...
0 jython
1 java
2 python
3 jruby
4 groovy

굳이 색인을 쓸 필요가 없으면 빼버리는 것이 구문을 좀 더 깔끔하게 할 수 있다.

>>> myList = ['jython', 'java', 'python', 'jruby', 'groovy']
>>> for item in myList:
...     print item
...
jython
java
python
jruby
groovy

이제 파이썬 언어에서의 조건과 반복 구조를 학습했다. 하지만, 가능한 단순하게 만드는 것이 좋은 프로그래밍 방법이며 그렇게 하지 않으면 논리를 쫓아가기가 너무 힘들어질 것이다. 적절한 코딩 기법을 익히는 데에는, 목록, 사전, 그 외에 반복에 활용할 수 있는 구조들을 알아두는 것이 도움이 된다. 그러한 컨테이너들을 사용하여 반복을 처리하는 것은 매우 유용한 전략이다. 사전 개체를 반복에 사용하는 예를 들어보겠다.

예제 3-25. 컨테이너에 대하여 반복

# Define a dictionary and then iterate over it to print each value
>>> my_dict = {'Jython':'Java', 'CPython':'C', 'IronPython':'.NET','PyPy':'Python'}
>>> for key in my_dict:
...     print key
...
Jython
IronPython
CPython
PyPy

반복할 때마다 my_dict.values()를 호출함으로써 사전 개체의 값을 얻어낼 수 있다는 것도 알아두면 유용하다.

예제 코드

이 장에서 논의했던 프로그램 흐름을 사용하는 예제 프로그램을 살펴보도록 하자. 예제 프로그램에서는 단순히 외부 텍스트 파일을 사용하여 운동 경기 팀의 선수 목록을 관리한다. 이 예제를 통하여 적절한 프로그램 구조와 공백의 효과적으로 사용하는 법을 알 수 있을 것이다. 또한 raw_input() 함수의 사용을 통하여 실제로 파일을 어떻게 사용하는지 볼 수 있을 것이다.

예제 3-26. os 모듈 들여오기

import os

# 빈 사전 생성
player_dict = {}
# 빈 문자열 생성
enter_player = ''

# 키보드로부터 정보를 입력받는 것을 되풀이
while enter_player.upper() != 'X':

    print 'Sports Team Administration App'

    # 파일이 존재하는 경우, 그것을 관리하도록 허가하고, 그렇지 않은 경우 강제로 생성.
    if os.path.isfile('players.txt'):
        enter_player = raw_input("Would you like to create a team or manage an existing team?\n (Enter 'C' for create, 'M' for manage, 'X' to exit) ")
    else:
        # 아직 존재하지 않는 경우 파일을 강제로 생성
        enter_player = 'C'

    # 어떤 조치를 취할 것인지에 대한 결정을 확인.  C = 생성, M = 관리, X = 저장하고 종료
    if enter_player.upper() == 'C':

    # 팀의 선수를 입력
        print 'Enter a list of players on our team along with their position'
        enter_cont = 'Y'

        #  새로운 선수를 입력하는 동안, 다음을 수행
        while enter_cont.upper() == 'Y':
            # 키보드 입력을 취하여 name 변수에 할당
            name = raw_input('Enter players first name: ')
            # 키보드 입력을 취하여 position 변수에 할당
            position = raw_input('Enter players position: ')
            # 사전의 열쇠인 선수 이름에 대하여 position을 할당
            player_dict[name] = position
            enter_cont = raw_input("Enter another player? (Press 'N' to exit or 'Y' to continue)")
        else:
            enter_player = 'X'

    # player.txt 항목을 관리
    elif enter_player.upper() == 'M':

        # 외부 파일로부터 값을 읽어 사전 개체에 할당
        print
        print 'Manage the Team'
        # 파일을 열어 playerfile에 할당
        playerfile = open('players.txt','r')
        # 파일의 항목에 대하여 반복을 하기 위해 for 되풀이를 사용
        for player in playerfile:
            # 항목을 열쇠/값 쌍으로 분할하여 목록에 추가
            playerList = player.split(':')
            # 파일로부터의 목록 값을 사용하여 사전을 만듦
            player_dict[playerList[0]] = playerList[1]
        # 파일을 닫음
        playerfile.close()

        print 'Team Listing'
        print '++++++++++++'

        # 사전의 값에 대하여 반복하며 열쇠/값 쌍을 출력
        for i, player in enumerate(player_dict):
            print 'Player %s Name: %s -- Position: %s' %(i, player, player_dict[player])

else:
    # 외부 파일을 저장하고 자원을 닫음
    if player_dict:

        print 'Saving Team Data...'
        # 파일 열기
        playerfile = open('players.txt','w')
        # 사전의 각 항목을 파일에 기록
        for player in player_dict:
            playerfile.write('%s:%s\n' % (player.strip(),player_dict[player].strip()))
        # 파일 닫기
        playerfile.close()

이 예제에는 이 책의 처음 세 장에서 논의했던 모든 주제를 망라하였다. 앞서 기술한 바와 같이, 운동 선수의 목록을 생성하고 그들의 상대적인 위치를 관리하는 것에 주안점을 두었다. 예제는 사용자가 exit 명령을 입력할 때까지 수행되도록 while() 되풀이에 들어서는 것에서 시작한다. 다음으로, ‘players.txt’파일이 존재하는지 확인한다. 그러할 경우, 프로그램은 다음으로 취할 행동을 결정하도록 사용자에게 코드 입력을 요구한다. 그렇지만, 파일이 존재하지 않는 경우 사용자는 최소한 한 쌍의 선수/포지션을 담은 파일을 생성하여야 한다.

계속해서, 프로그램은 사용자로부터 필요한 만큼의 선수/포지션 쌍을 입력받을 수 있고, 언제든지 종료할 수도 있다. 사용자가 선수/포지션 목록 관리를 선택하면, 프로그램은 그저 ‘players.txt’ 파일을 열고, for() 되풀이를 통해 파일 내의 각 항목을 훑는다. 되풀이할 때마다 나타나는 선수로 사전이 채워진다. 일단 되풀이가 완료되면, 파일은 닫히고 사전이 반복 및 출력된다. 프로그램을 벗어나면 else() 구문이 호출되어, 사전의 각 선수에 대하여 돌아가면서 파일에 쓰게된다.

불행하게도, 이 예제는 너무 단순할 뿐더러 함수(4장) 또는 클래스(6장)에 대한 지식이 없이는 구현할 수 없다. 이러한 주제에 대해 습득한 후에 이 예제를 다시 살펴보고 기능을 추가하거나 단순화시켜본다면 좋은 연습이 될 것이다.

요약

모든 프로그램은 서술과 식으로 구성된다. 이 장에서 우리는 식을 만들고 사용하는 법을 자세히 다루었다. 수식은 수학 연산과 비교를 조합하여 만들어낼 수 있다. 이 장에서 우리는 우리의 프로그램에서 수학 연산자를 사용하기 위한 기초를 논의했다. __future__ 부분에서는 __future__의 기능을 사용하는 법을 소개했다. 다음으로는 비교 및 비교 연산자에 대해 공부했다.

적절한 프로그램 흐름과 if 문, 그리고 파이썬에서의 서로 다른 되풀이 구조에 대해 논의함으로써 짧은 장을 마무리했다. 다음 장에서는 함수를 작성하는 방법과, 많은 내장 함수를 활용하는 방법을 논의하겠다.