Saturday, October 2, 2010

python 1부 : 문법

난 파이썬 유져다. 그러나 아직 단 한번도 제대로 python 기초 책 조차 들여다 본적이 없다. 2년전 쯤에 이화여대에서 agile 수업이 있을때 강사에게 python의 실력을 좀더 늘리려면 어떻게 해야 하겠냐는 질문에 그 강사는 우선 한번 python 기초 책을 첨부터 끝까지 훑어보라 이야기 했다. 2년이 지난 지금에서야 그 일을 시작하려 한다. 너무 먼 길을 돌아온 기분이다. C를 보고 C++을 보고 이제야 내 주종목을 보려한다. 감회가 새롭다. 기대도 높다. 흥분된다.


목표하는 것은 2주 안에 책을 정독하고 다른 블로그에서 찾은 berkeley 에 한 bioinfomatician이 만들어 놓은 methylC 코드를 분석해서 내가 필요로 하는  BS-seq 분석 파이프라인을 갖추는 것이다. 항상 그렇듯 새로이 어떤 공부를 시작하면 즐거운 긴장감이 감돈다. 특히 이번 공부는 그 흥분감이 강렬하다. 매우 기분좋다. 마치 소개팅 나가듯. go go !


선택한 text는 오른쪽 그림의 책이다. 원래는 프리렉의 것을 하려고 했으나 이 책 저자의 블로그를 보고 느낀 그의 이미지에 이 책을 선택했다.


내가 몰랐던 내용만 요약한다.


chapter1
이 책은 python 3 버젼에 근거 하고 있다. 우선 2.x 버젼과의 차이를 알아본다.
print 가 함수 형태로 변했다. print 'good'은 3 버젼에서는 통용되지 않고 print('good') 이어야 한다. 또한 함수화 되었기 때문에 추가적 매개변수의 전달이 가능하다. print('welcome to','python',sep='~',end='!')이라고 하면 welcome to~python! 으로 출력이 된다.
long 형이 없어지고 int형으로 통일 되었다.
int나누기 int는 float 형이 된다.
2.x 에서는 일반 스트링이 인코딩이 있는 문자열이었고 유니코드가 따로 있었는데  3 버젼에서는 유니코드를 따로 지정하지 않고 일반 스트링이 기존의 유니코드와 동일하며, 인코딩이 있는 문자열은 bytes로 표현된다.
2.x 버젼으로 짜여진 코드를 3 버젼으로 바꾸기 위해서 python 에 들어 있는 2to3를 이용할 수 있다.

chapter2 : 자료형 및 연산자
// : 정수 나누기, 2//3 하면 0이 된다.예전의 /와 같은 역할.
인덱싱을 이용한 문자열 변경은 허용되지 않는다. a='python';a[0]='a'하면 에러.
ord() : 문자의 유니코드를 출력, chr(): 유니코드의 문자를 출력
리스트에 값을 추가 할때 append 하면 맨 마지막에 insert를 이용하면 원하는 위치에 값을 넣을수있다.
3 버젼에는 set이라는 자료형이 추가 되었다. 이는 거의 리스트와 유사하며 단 순서가 없다. 그리고 정의는 {}를 이용한다. set형은 index를 제공하지 않는다. 메서드는 리스트와 거의 유사하며, 추가적으로 교집합과 합집합을 구할수 있다. a = {1,2,3} ; b = {3,4,5}와 같이 a,b 두개의 set이 정의되어 잇을때 a.union(b)하면 합집합을 a.intersection(b)하면 교집합을 구하는 메서드를 제공한다. -는 차집합을 |는 합집합을 & 는 교집합을 뜻하는 연산자도 제공한다.
튜플은 읽기 전용으로 그만큼 빠르다. 튜플을 이용하면 변수가 하나 더 필요한 swap 예제가 간단히 해결된다. a,b=1,2; (a,b) = (b,a)
2.x 버젼에서는 사전 자료형의 items(),keys(),values()의 리턴 값이 리스트 였는데 3버젼에서는 dict 객체로 변경되었다. 그래서 list화 하고 싶으면 list() 생성자를 이용해야 한다. 사전의 삭제는 del 문을 이용해서 하나씩 삭제할수도 있고, clear()메소드를 이용해서 전부 삭제할수도 있다.
0 과 '' 과 None은 False를 뜻하고 나머지는 True를 뜻한다.
객체의 고유한 아이디를 출력하기 위해 id() 함수를 이용할수 있다.
리스트를 제외한 객체를 복사 할때는 copy모듈을 사용하여 shallow copy가 되며(리스트의 경우 = 연산자를 이용하면 같은 객체를 가르키게 된다.) deepcopy를 위해선 deepcopy()함수를 사용해야 한다. copy() , deepcopy()는 copy 모듈에 있다. 부가적인 설명을 하자면 a=1; b=a 일때 b는 a의 shallowcopy를 한다. 이는 int형이라 문제가 없지만 예를 들어 a = [1,2,[3,4]] ; b = copy.copy(a)로 shallow copy를 할경우 a 변수 안에 동적 할당한 [3,4]가 새로이 생성되어서 copy된 것이 아니기 때문에 b 역시 [3,4] 는 같은 객체를 가르키고 있다. 그래서 a[1].append(5)를 하게 되면 a, b 둘다 [1,2,[3,4,5]]가 된다.

chapter3 : 함수
def는 함수 객체를 만들겠다는 키워드이다. 함수 선언부에 return이 없으면 None이 return된다.함수 이름은 함수 객체를 참조하는 레퍼런스이다. 원래 함수는 return 값이 하나이지만 여러개의 return이 가능한 것처럼 보이는 것은 여러개의 값을 튜플 객체로 만들어 리턴하는 것이기 때문이다. 함수의 매개 변수 전달은 C의 call by reference와 유사하나 다만 수치형 데이터와 같이 매개 변수가 변경 불가능한 경우는 call by value와 같이 행동한다.(요부분은 확인 필요, 저자의 질문답변시 추가할 예정)
질문에 대한 저자의 답변 : http://groups.google.com/group/python3/browse_thread/thread/173957a484c24206/1c768deb2057dafe#1c768deb2057dafe
함수의 매개변수의 갯수가 가변적일때 *를 이용할수 있다. def func(*args):라고 하면 func에 넘겨주는 매개 변수가 정해지지 않은 것이고 이는 튜플 형태로 처리된다. 사전 형태로 가변 인수를 넘기고 싶을때는 함수를 선언할때 매개 변수 앞에 **를 붙이면 된다.
람다 함수는 함수이름(레퍼런스) 없이 함수 객체만 생성하는 것으로 그 형태는 lambda 인수 : <구문> 으로 lambda x,y : x*y 식으로 사용할수 있다.
리눅스의 man페이지 처럼 모듈이나 함수의 내용을 알고 싶을때 help()함수를 이용할수있다. 이때 출력되는 내용은 객체 안에 있는 __doc__속성이다. 이는 모든 객체의 부모인 object에 포함된 기본속성이다. 예를 들어 func이라는 함수가 있을때 __doc__의 내용을 func.__doc__ = "..." 으로 직접 적을 수도 있고 func 를 정의 할때 그안에 "나 """를 이용해서설명을 적으면 자동으로 __doc__ 안에 저장된다.
interator (이터레이터) : 순회가능한 객체(list, string등)이 for와 같은 문에서 작동할때(예 : a = 'abc'; for i in a:print(i)) 이터레이터 객체를 불러서 이터레이터 안에 __next__() 메소드를 실행하여 객체 안의 값을 하나씩 리턴할수 있게 된다. 예제가 수행되는 과정은 it = iter(a); it.__next__();의 반복으로 이뤄지는 것이다.
generator(제너레이터) :
http://groups.google.com/group/python3/browse_thread/thread/16096054ad5a4631/eb4725f8f41984cb#eb4725f8f41984cb

chapter4 : 제어
조건문에서 70 <= score < 80 과 같은 것이 허락된다.
and 와 &, or와 |은 동일한 연산을 하나 and와 or의 경우 단축 평가를 된다.
보통 리스트 항목과 인덱스 값을 동시에 얻을때 인덱스 값을 먼저 얻고 그 것을 이용해서 리스트의 아이템을 얻어내는데 enumerate 함수를 이용하면 간단히 해결할수 있다. enumerate('시퀀스 타입객체'[,'시작값'=0]) 의 형태로 호출하며 호출 결과 튜플 형태인 (인덱스,시퀀스 객체의 아이템) 이 리턴된다.
기존 리스트 객체를 이용하여 조합, 필터링등의 추가적인 연산을 통해 새로운 리스트 객체를 생성할때 list comprehensions(리스트 내장)을 이용하면 좋다. 형태는 [<표현식> for <아이템> in <시퀀스 타입 객체> (if <조건식>)] 이며 예는 L1 = [1,2,3,4,5]; [i**2 for i in L1 if i**2] 이다. [x*y for x in L1 for y in L2] 도 가능하다.
리스트 안의 값을 if문으로 필터링 하는 방법과 동일하게 filter() 함수를 이용할수 있다. 그 형태는 filter(| None, <이터레이션이 가능한 자료형>) 으로 def bigThan20(i): return i>20; L = [10,30,50] ; IterL = filter(bigThan20,L) 하면 IterL에는 10 만 있는 이터레이터 가 들어 있다. 즉 반환값으로 이터레이터를 반환한다.
zip()함수는 이터레이터를 지원하는 클래스 여러개를 묶어서 zip형의 객체를 반환하며 반대로 묶여 있는 zip형 클래스 앞에 *을 붙여서 zip()을 호출하면 객체를 분리시킨다.
map()함수를 이용하면 이터레이터가능한 객체의 값들을 함수에 적용시켜 값을 리턴한다.
L = [1,2,3]이 있을때 이를 출력하는 방법은 for i in L: print(i)가 있고 print('\n'.join(i for i in L)) 이 있는데 후자의 것이 print함수를 한번만 호출하기 때문에 성능이 탁월하다.