생물정보학/Python Study

3. 파이썬 제어구조 - 파이썬(Python) 배우기

케이든 2014. 10. 15. 20:30

 

3. 파이썬 제어구조 - 파이썬(Python) 배우기

 

 

프로그래밍 언어가 제공하는 핵심 기능중 하나는 제어 흐름을 기술할 수 있도록 하는 것으로 해야할 작업을 차례대로 나열하는 순차적 흐름, 특정 조건에 따라 True 또는 False로 흐름을 변경하는 조건적 흐름, 일정한 조건을 따르는 반복 흐름을 들 수 있는데 이런 흐름 제어의 중심에 비교 연산, 논리 연산이 자리 잡고 있으며 앞선 파이썬의 변수와 타입 - 파이썬(Python) 배우기에서 스칼라 - Bool 타입에 대한 설명에서 기본적인 내용을 다루었다. 몇가지 꼭 기억할 사항 옮겨보면 아래와 같다.
 
파이썬에서는 TrueFalse라는(대소문자 주의) 특별한 값을 제공하고 있으며 파이썬에서는 False, 0, None, '', "", (), [], {} 등을  모두 False로 처리한다. 아래는 관련 연산자들이다.

  • == 양쪽이 같으면 True.
  • <, <= 우측 보다 작으면, 작거나 같으면 True
  • >, >= 우측 보다 크면, 크거나 같으면 True
  • a<b<c 복합 조건으로 다른 언어에서는 and로 묶어서 처리해야 했으나, 파이썬에서 간편하게 기술하면서도 프로그램을 읽기 쉽도록 했다.
  • !=, <> 양쪽이 다르면 True. 파이썬에서는 두가지를 모두 사용할 수 있다.
  • and 양쪽이 모두 True 이면 True. 단, and와 or 연산자는 항상 True와 False 값만 리턴하지 않는다. 좌에서 우로 비교해 가는데 최종 비교 대상 값을 그대로 리턴한다. 예를 들어 a and b에서 a가 False로 판단되면 a값 그대로를 리턴한다. a or b에서 a가 True라면 b를 볼필요가 없으므로 a 값을 그대로 리턴한다. 이것을 문장에 응용한 예가 있는데 아래의 예를 보면 a가 0인 상태로 나눗셈을 하면 "ZeroDivisionError"가 발생한다. 그런데 and 연산자 앞에 a가 0이 아닌지를 확인하는 코드를 삽입하여 a가 0이면 False를 리턴하고 우측은 비교할 필요가 없으므로 나눗셈을 하지 않는다. 0이 아니라면 나눗셈 결과를 그대로 리턴한다.
  • or 둘중에 하나만 True 이면 True.
  • not 우측 bool 값을 반대로. True면 False. False면 True
  • a is b a와 b 동일한 메모리를 참조하고 있으면 True를 리턴한다. 스칼라 타입은 값이 같다면 동일한 메모리를 참조하지만, 리스트의 경우에는 값이 같이 보여도 각각 따로 저장하고 변수를 참조하는 경우만 동일한 메모리를 참조한다.

 

* 들여쓰기를(Indentation) 통한 블럭 구분

C, Java, Pascal등 다른 언어에서는 if, while등의 블럭이나 함수의 블럭을 {}나 begin-end블럭으로 구분하는데 파이썬에서는  들여쓰기를 통해서 블럭을 구분한다. 몇가지 원칙이 있는데, 타언어에 익숙한 프로그래머의 경우 처음에는 익숙치 않을 수 있지만 코드를 읽기 쉽게 하고 오류를 줄일 수 있는 좋은 방법이라는데 공감하게 될 것이다. 아래는 파이썬의 들여쓰기를 통한 블럭 구분의 몇가지 중요한 원칙이다.

  • 최상단 블럭, 즉 프로그램을 시작할 때의 블럭은 1컬럼에서 기술한다.
    초창기 펀치 카드 시스템으로 코볼이나 포트란을 구동시킬 때의 규칙을 연상시키지만....
  • 동일 블럭은 동일한 들여쓰기를 갖는다.
    들여쓰기 크기는 제한이 없지만 내부 블럭은 블럭을 내포하는 상위의 문장 기준으로 동일 크기의 들여쓰기가 필요하다.
  • 탭 문자 크기에 따라 공백 문자를 대치할 수 있다.
    탭 문자 1개당 공백 8개를 설정한 경우 탭 1개로 들여쓰기 한 것과 공백 8개로 들여쓰기 한 것이 같은 효력을 미친 다는 것이다. 그러나, 시스템의 탭 설정에 따라 혼란이 있을 수 있으므로 섞어쓰기를 지양한다. IDLE에서의 탭설정은 Options>IDLE Configuration>Fonts/Tabs 탬에서 Indentation Width로 설정한다.
사용자 삽입 이미지


 

* 대입/치환문


"변수 = 표현"의 가장 기본적인 문장 형태로 변수에는 tuple의 원소와 같이 변경 불가한 변수를 제외한 여러 변수들이 올 수 있다.
여러 변수에 동일한 값을 대입할 경우에는 a = b = c = 10 같이 기술할 수 있다.
C언어에서 처럼 a = a + 1같이 대입 연산자 양쪽에 동일 변수가 있는 경우에는 += 형태의 대입연산자를 사용할 수 있다. 

  • 다중 대입문 : = 양쪽에 여러개의 변수, 여러개의 표현을 한번에 기술하는 것으로 ,(comma)로 구분하며 양쪽의 변수 및 표현의 개수는 동일 해야 한다. 다중 대입문을 사용하면 아래의 예와 같이 간단하게 swap을 기술할 수 있다.

>>> a, b = 1, 4
>>> a, b = b, a
>>> print a, b
4  1

  • 다중 대입문과 배열 : 위에서 언급한 다중 대입문은 결국 우측의 표현들이 튜플 형태로 왼쪽에 전달 되는 것으로 아래와 같이 명시적으로 리스트를 전달해도 마찬가지 결과를 얻을 수 있다.

>>> c = [1, 4]
>>> a, b = c # a, b = [1, 4]도 동일 효과
>>> a, b = b, a
>>> print a, b
4  1

* if - elif - else

가장 기본적인 제어 구조로 아래와 같은 기본 문형을 갖는다.

if 조건:
코드 블럭
elif 조건:
코드 블럭
else:
코드 블럭 
  • elif와 else는 흐름에 따라 기술하지 않을 수 있고, elif의 경우에는 여러 개의 elif 조건으로 다중의 선택 조건을 기술 할 수 있다.
  • 콜론(:)으로 조건의 끝과 코드 블럭의 시작을 지시한다.
  • 코드 블럭에 또 다른 if - elif - else를 포함할 수 있다.
  • 코드 블럭이 단일 문장인 경우에는 콜론(:) 다음에 연속해서 코드를 기술 할 수 있다.
  • if - elif - else는 동일 컬럼에 기술 되어야 한다.
  • 코드 블럭에 아무 것도 할 것이 없을 때는 pass 문장을 기입 할 수 있다.

if n%2 == 0:
    pass
else:
    count += 1

  • "assert 조건"으로 프로그램 실행중 반드시 True 상태 임을 검증할 수 있다. 만약 조건이 True가 아니면 AssertionError 예외를 발생시킨다.
  • if - else를 조건문이 아니라 C나 Java언어의 변수 = (조건 ) ? :; 처럼 연산자로 사용할 수 있다. 형식은 "True값 if 조건 else False값"으로 사용하여 문장을 좀더 단순화 시킬 수 있다. average = sum/count if count != 0 else None 문장은 아래의 문장을 if-else 연산자로 줄인것이다.

if count != 0:
    average= sum/count
else:
    average= None

 

* for 반복문

다른 언어에서 for 문은 초기값, 조건, 증분등의 요소로 반복 수행할 블럭을 묶지만 파이썬에서는 수행할 대상(리스트)이 정해진 경우에 리스트의 각 원소들을 차례대로 반복 수행하기 위한 용도로 사용한다. Perl이나 PHP의 foreach와 유사한 형태로 동작하며 아래와 같은 기본 문형을 갖는다.

for 변수 in 리스트:
코드 블럭


 리스트에는 파이썬 배열 타입(튜플, 리스트, 사전)의 변수나 값, 또는 배열을 리턴하는 함수도 올 수 있다. 앞선 포스팅의 배열(Array)에서 다루었던  단순 숫자 리스트를 리턴하는 range() 함수를 사용하면 다른 언어의 for 문과 유사한 동작 형태를 보다 간단하게 기술할 수 있다.
range(i, j, k)는 i 위치 부터 j위치까지(j위치는 포함되지 않는다. 직전 위치의 항목까지만) k만큼씩 움직이면서 해당하는 정수를 원소로 하는 리스트를 리턴한다. range(j)는 range(0, j)의 의미로 사용한다. k가 음수면 역방향 진행을 의미한다.
for 다음의 변수에는 루프 운행과정의 각 원소 값이 입력되어 코드 블럭에서 사용하는 형태가 된다. 아래의 3가지 문장은 동일한 효과를 낸다.

for o in range(1,36,2):
    print o

for o in [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35]:
    print o

ary_a = [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35]
for o in ary_a:
    print o

  • 코드 블럭이 한 문장으로 간단하다면 콜론(:) 다음에 기술할 수 있다.
  • 코드 블럭은 Indentation 규칙을 준수해야 하고 또다른 for 문장을 내포할 수 있다.
  • break 문장을 사용하면 코드 블럭에서 나올 수 있고
  • continue 문장을 사용하면 코드 블럭의 나머지를 수행하지 않고 다음 원소에 대한 루프를 계속할 수 있다.
  • 배열내에 배열이 있는 경우에는 for 와 in 사이에 리스트 참조 형태로 기술하여 내부 배열을 손쉽게 참조할 수 있다. 

>>> a = (('name', '홍길동'), ('age', 30), ('sex', 'male'))
>>> for fld, val in a:
print fld, val

name 홍길동
age 30
sex male

 

* while 반복문

지정한 조건이 True일 동안 프로그램을 반복 수행할 경우에 사용하며 기본 문형은 아래와  같다.

while 조건:
코드 블럭
else:
코드 블럭 
  • else: 이하는 최초 조건 비교시 True가 아닐 경우에만 수행되고 생략할 수 있다.
  • break 문장을 사용하면 코드 블럭에서 나오는데 else:문이 있어도 else:문을 수행하는 것은 아니다.
  • continue 문장을 사용하면 조건 비교로 다시 이동한다.

sum = 0
i = 0
while i < 10:
i += 1
sum += i
print i, sum

 

* 주석문과 기타 제어문

프로그램이 커지고 복잡해지면 가독성이 떨어지게 마련인데 이를 보완해 줄 수 있는 방법이 코드에 주석을 달수 있게 한 것이다. 파이썬에서는 # 문자로 주석을 표시한다. 단, 따옴표 바깥에 표시해야 한다.
줄의 맨처음에 #를 표시하면 해당 줄은 모두가 주석이고 줄의 중간에 오면 그 이후가 모두 주석이다. 

  • 코딩 스키마 주석 : 프로그램의 첫줄이나 두번째 줄에 기술하며, 코드의 인코딩을 기술한다. 코드중에 한글 인코딩(euc-kr)이 있을때는 다음과 같이 기술한다. # -*- coding: cp949 -*- .
  • 쉘 인터페이스 : 리눅스/유닉스 환경에서 쉘로 하여금 어떤 파이썬 인터프리터를 사용할 것인지를 경로 지정으로 지시하는 방법이다.
    #!/usr/bin/env python 으로 사용할 수도 있고 #!/usr/bin/python 처럼 직접 경로를 지정할 수도 있다. 이렇게 쉘 인터페이스를 기록한 파이썬 코드는 사용자가 파일명만으로 프로그램을 실행시킬 수 있다.
  • 여러줄에 걸친 문장 기술 : 한 문장을 여러 줄에 걸쳐 기술할 때는 줄 맨 끝에 \를 입력하고 다음줄에 내용을 이어가면 된다.
  • 배열 및 사전의 원소 들은 \ 없이 여러줄에 걸쳐 입력할 수 있다. (), [], {}
  • 빈 줄에 대한 처리 : 코드 블럭이 특정 문장에 내포된 상태로 여러 라인에 걸쳐 기술되고 있을 때 빈 줄은 해당 블럭의 끝을 표시하므로 하나의 블럭은 들여쓰기 상태에서 연속해서 기술해야 함을 주의한다.

 

* 기본적인 입출력 및 코드 실행

프로그램이 GUI환경인지, 웹 환경인지 아니면 단순 IDLE 환경인지에 따라 입출력 방법의 차이가 매우 크다. 본 포스팅에서는 학습이나 기타 디버깅의 용도로 간단하게 입출력하는 방법을 다루고자 한다.

  • raw_input('메시지') : 표준 출력으로 메시지를 보내고 표준 입력으로 부터 입력을 대기한다. 한줄을 입력받으면(엔터로 종료) 입력 받은 값을 그대로 스트링으로 리턴한다. 사용자가 숫자로 입력해도 리턴 값의 타입은 스트링이다. '메시지'는 생략할 수 있다. 

>>> type (raw_input('input???'))
input???123
<type 'str'>

  • input('메시지') : raw_input과 동일하게 사용자 입력을 받지만, 입력 받은 스트링을 그대로 리턴하는 것이 아니라 입력 받은 내용을 파이썬 문장으로 처리한 결과를 리턴한다. 즉, 리턴 받은 문장에 대하여 eval(스트링)을 처리한 결과를 리턴한다. 그래서, 사용자가 오류를 입력하면 프로그램에 심각한 영향을 줄 수도 있으므로 사용에 주의해야 한다. 아래의 예를 보면 a+1 입력이 단순 스트링이 아니라 변수 a를 참조하는 결과임을 확인할 수 있다.

>>> a = 10
>>> print input('input=')
input=a+1
11

  • print 표현,표현... : 표현으로 지정한 내용을 표준 출력으로 내보낸다. print 한 문장을 한 줄에 출력하는데 문장 끝을 ,(comma)로 끝내면 다음 print 문장은 새로운 줄의 시작이 아니라 바로 옆으로 이어서 출력한다.

>>>print "10/3 = ",
>>>print 10/3
10/3 = 3

print는 기본적으로 표준 출력으로 내용을 출력하는데 표준 출력이 아닌 표준 에러(stderr)등으로 출력 방향을 바꿀때(redirecting) sys 모듈을 import한후 print >>sys.stderr, 표현, 표현...... 또는 print >>sys.stdout, 표현, 표현......으로 사용하면 된다. 아래의 그림은 인위적으로 stderr로 메시지를 보낸것과 표준 출력으로 보낸것을 비교한 결과이다.


 

import sys
print >>sys.stderr, "This is an error message"
print "This is stdout"
print >>sys.stdout, "This is also stdout"


 

사용자 삽입 이미지

 

  • eval('표현식') : 지정한 파이썬 표현식을 실행하고 그 결과를 리턴한다. 위에서 언급한 input()함수에서 사용자 입력후 수행하는 함수가 바로 eval()로 내부의 식은 대입식의 우측에 기술하는 식으로 한정하고 문법 오류가 없어야 한다.

>>> a = 10
>>> eval (' a+ 5')
15

  • exec('문장') : eval은 지정한 식을 실행하고 그 결과를 리턴하지만, exec는 지정한 문장을 실행하는 것에 초점을 두고있다. 즉, eval()에는 a= b+10과 같은 완전한 문장이나 print 문이 들어갈 수 없지만 exec는 여러줄의 문장도 가능하고 제한없이 문장들을 실행시키지만 대신 값을 리턴하지는 않는다.

 

  • compile('표현식 또는 문장', 파일명, 식의 종류) : eval 또는 exec를 반복적으로 수행할 경우 해당 코드를 미리 컴파일하여 수행 속도를 높이기 위한 것으로 컴파일한 코드 오브젝트를 리턴하고 해당 오브젝트를 가지고 eval() 또는 exec문에 사용하면 된다.
    파일에서 코드를 불러오지 않는 경우에는 파일명에 '<string>'를 입력한다. 식의 종류는 그 내용에 따라 'eval' 이나 'exec'로 지정하면 되고 exec의 경우 한문장인 경우에는 'single'로 지정하면 된다.

>>> a = compile ('10+1','<string>','eval')
>>> print a
<code object <module> at 01244188, file "<string>", line 1>
>>> eval (a)
11

* 예외 처리(Exceptions)

예외는 정상적인 프로그램 흐름을 중단시키는 이벤트로 예외 상황이 발생하면 파이썬은 try 코드블럭에 해당하는 except 절을 찾고 해당하는 except절이 없으면 프로그램을 중단하고 메시지를 표준 에러(stderr)로 보낸다. 사용하기 위한 문법은 아래와 같다.

try:
코드블럭
except 예외코드, 예외오브젝트:
코드블럭
except (예외코드,예외코드):
코드블럭
except:
코드블럭
else:
코드블럭
finally:
코드블럭


 
except 다음에 예외코드나 오브젝트가 없으면 모든 예외를 처리하겠다는 구문이다. 예외오브젝트는 생략할 수 있고 기술되면 예외오브젝트가 전달된다. 여러가지 예외를 한 코드블럭으로 처리할때는 튜플로 묶어서 전달한다.

else절을 기술하면 예외가 발생하지 않은 경우에 처리된다.
finally절을 기술하면 예외 발생 여부에 관계없이 try절 수행이후 수행한다.
 
 시스템에서 발생시킨 예외가 아니라 프로그램 수행도중 사용자가 예외를 인위적으로 발생시킬수 있는데 "raise 내장예외코드,예외객체" 또는 "raise 클래스인스턴스,예외객체"의 방식을 사용한다. 예외객체는 생략할 수 있으며 raise의 대상이 되는 클래스는 반드시 Exception 클래스를 상속 받아야하고 인스턴스 오브젝트를 raise문에 적용한다. except절에 사용할 예외코드로는 클래스명을 사용한다.

 

[출처] - |주|동운시스템 http://www.dongwun.com/tc/144