Thursday, October 14, 2010

linux command

--send message in linux:
* 메세지 브로드캐스트
  wall 메세지
  ctrl+D

*개인에게 메세지 보내기
  write userid pts/number
  ctrl+d

--display cpu information:
cat proc/cpuinfo

--mail in linux (when sendmail server is running) : 
-check mail : mail
-writing mail :
mail graphy21@gmail.com
subject: something
something
.

2*(ctrl+c) mean cancel.
-writing mail by file : mail -s 'subject' graphy21@gmail.com < some_file.ext

--check running demon in kernel:
ps acx
-check zombi process & kill that:
ps axj # 'z' in STAT field in output mean zombi
kill -l # list of signal which can be send to process
kill [-signal number || signal] PID # example : kill -9 973

Tuesday, October 12, 2010

리눅스 운영체제의 이해와 개발

병렬 프로그래밍을 보다가 thread를 이용해서 posix에서 알아서 cpu를 조정하는 것을 알았다(우스워 보일지 모르지만 나름 굉장한 흥분된 발견이였다. python 예제에서 나왔을 때 아무 생각 없이 따라 했는데 연대 핸드아웃 프린트 물 보다보니 확실히 이해하는 순간... 여튼). 그러면 순간 드는 생각이 아니 그럼 os는 어떻게 알아서 thread를 관리하지? 라는 생각. 그래서 os가 어떻게 돌아가는지 알아야 겠다는 생각에 선택한 책.
상품평에 의하면 완전 쉬운 해설과 친절한 최고의 책과 같이 되어있지만... 나와 같은 허접덩이한테는 그저 외계어에 가깝다.
그런데 재밌는건 이해가 거의 안되지만(하다못해 처음 kernel을 다운 받아서 컴파일 하는 과정 조차 이해되지 않음) 왠지 모르게 재밌게 읽고 있다는 것이다. 신기..
내 목표는 이해와 개발이 아니라 끝까지 정독과 개념 잡기 정도 이다. 끝을 꼭 보자. 끝을 보도 책에 대한 나의 평가를 다시 붙이도록 한다.

Sunday, October 10, 2010

evolution of methylome

영건씨랑 이야기 하다 좀더 정리가 된거 같아서 관련 내용을 기록한다.

목표 : methylation의 evolution의 경향

기존의 논문은 dna sequence로 phylogeny tree를 만들고 그걸 기준으로 methylation pattern을 정리 한다. 정해진 트리에 맞춰서 transposon (repeat sequence) 과 genebody (혹은 exon과 intron 부위를 나눠서) methylation pattern(여러종을 보기때문에 CpG 뿐만 아니라 CHH등)을 보고 DNMT의 homologue 의 존재 여부와 연관을 지어 phylogeny의 어느 branch는 어떤 시퀀스(CG나 CHH)가 많이 methylation되어 있고 어느 부분은 그렇지 않다라는 식으로 정리한다.
우리는 아예 phylogeny를 methylation이 들어간 정보로 새로히 그려보고자 한다. 그리고 그 tree를 그리기 위한 genomic region을 크게 gene body 와 transposon으로 나누고 두 영역으로 그린 tree가 같은가를 본다.
FFP를 선택한 이유에 대해서는 기존의 multiple alignment를 통한 phylogeny tree를 그리는 것은 homology가 있는 시퀀스를 선택해서 그려야 하는데(특정 부위에 의한 biased가 있는게 아닐까 생각한다) 이러한 제약이 없다.

Wednesday, October 6, 2010

python 3부 : 모듈

이부분은 chapter의 순서와 상관없이 나가겠다. 보고 싶은것 부터..

chapter18 : 보다 견고한 코드 만들기
이부분에서는 unittest 모듈을 사용하는 방법을 알려준다.
일반적으로 unittest의 TestCase의 클래스를 상속 받는 클래스를 생성한다음에 TestSuite 에 넣고 실행. 자세한것은 책 참조

아 보다가 알아낸것 None은 "is" 연산자를 이용해야 한다는데 아래 링크 참조
http://jaredgrubb.blogspot.com/2009/04/python-is-none-vs-none.html

chapter19 : distutils를 이용한 배포
ㅁㅁ

etc
- timeit 모듈 : 실행 시간 재는 모듈
http://man.lupaworld.com/content/develop/diveintopython-html-5.4/diveintopython-5.4/performance_tuning/timeit.html

python appendix

디버깅
파이썬에서 C언어 디버깅을 위한 유틸인 gdb 처럼 비슷한 pdb를 제공한다.
pdbtest 라는 모듈이 있을때 이것을 디버깅 하기 위한 모듈이 pdb.
import pdb, pdbtest 한뒤 pdb.runcall(pdbtest.func) 과 같이 하면 pdbtest 모듈의 func 함수를 테스트 해볼 수 있다. 디버깅 모드로의 실행은 run, runcall. runeval 이 있다.
b (break) : 브레이크 포인트설정및 확인
w (where) : 현재 스택 프레임 확인 등....
디버깅 모드(Pdb)에서 help를 치면 각종 명령어를 볼수 있으며 특정 명령어에 대한 설명을 보려면 help <명령어> 하면 된다.

Sunday, October 3, 2010

python 1부 : 문법 2

1부의 내용이 너무 길어지는 바람에 포스팅을 나누기로한다. 
책을 4장까지 밖에 보지 않았지만 지금까지 느낀점을 이야기 하자면 이 책의 포지션이 좀 애매하다는 것이다. 컴퓨터 언어를 전혀 모르는 이를 위해 쓴거 같지도 않고 그렇다고 심화해서 좀더 깊이 설명한것도 아니고 어중간하게 설명한다는 점이다. 글을 보다 보면 C를 알고 있다는 전제 하에 설명한거 같은데 그렇다고 뚜렷하게 각각의 요소를 C의 구성요소로 어떻게 만들었는지 설명하는 것도 아니고.. 애매하다. 


C로 만들었다고 하는데 객체 개념이 어떻게 들어갔는지, C++도 C로 만들은 것인지 그렇다면 궁극적으로 C는 무엇으로 만들었는지(어셈블리어라고 생각하고 있지만)등의 궁금증이 생긴다. 이것 또한 저자한테 질문을 해봐야 겠다. 어제 토요일 늦게 질문을 올렸는데 월요일에 답변이 오지 않으면 왠지 실망할거 같은 느낌이다.


chapter5 : 클래스
클래스의 선언은 함수의 선언과 유사하게 def 대신 class 키워드를 사용하면 된다. 클래스가 선언과 동시에 클래스 객체가 생성된다(클래스 이름의 이름 공간이 생긴다). 클래스를 사용하기 위해선 인스턴스 객체를 생성해야 한다. class MyClass:Name='shkm';def Print(self):print(self.Name) 이라는 클래스가 있으면 p1 = Myclass()와 같이 인스턴스 객체를 만들게 되고 이는 클래스의 객체와 모든 멤버변수와 멤버함수를 공유하게 된다. 다만 인스턴스 객체의 멤버변수가 변하게 되면 (p1.Name='eykang') 변경된 내용을 인스턴스 객체 이름 공간에 저장하게 된다.
클래스의 멤버변수,함수들은 default로 public으로 정의 된다. 위의 Myclass 에서 보면 self가 나오는데 이는 C++의 this와 같은 역할(자기 자신을 가르키는 포인터)을 한다. 이 self는 예약어는 아니지만(곧 다른 이름으로 표현 가능) 관행상 self라고 하며 '정적메소드'와 '클래스메소드'를 제외한 메소드의 첫번째 매개변수는 self로 취급된다.
그럼 self로 메소드를 나타내야 하는 이유? 인스턴트 객체도 클래스 객체의 메소드를 가리키고 있기 때문에 메소드를 사용하기 위해서는 자기 자긴을 가르키는 포인터를 넘겨줘야 한다. 그러나 그 메소드를 실행할때는 p1.Print() 처럼 정작 self에 대한 매개 변수를 넘겨주지 않는데 이는 자동적으로 self가 들어가게 된다. 이와 같은 호출을 바운드 메서드 호출이라 한다.
파이썬에서는 실행시간에 각 클래스와 인스턴스 이름 공간에 멤버 변수를 추가하거나 삭제할 수있다. 이는 서로 이름 공간이 분리 되어 있기 때문이라고 이해하면 된다. 클래스 객체에 새로운 멤버 변수를 추가하면 이는 인스턴스 객체에서 참조할수 있는데 이는 객체에서의 멤버 변수 탐색이 인스턴스>클래스>전역 순이기 때문에 자기 자신에 없는 멤버변수라도 클래스 객체에 생기면 참조할수 있는 것이다. 반면에 인스턴스 객체에 새로운 멤버변수를 추가하게 되면 자기 자신의 인스턴스 객체에서만 참조가 가능하다. 인스턴스 객체 내의 내장 속성 __class__를 이용하면 클래스 객체의 멤버변수를 바꿀수도 참조 할수도 있다.
한 클래스가 다른 클래스의 상속인지 확인하거나 한 인스턴스 객체가 어떤 클래스로부터 생성되었는지 확인하기 위해 isinstance(인스턴스 객체, 클래스 객체) 를 이용할 수 있다.
클래스 생성시 객체를 초기화 하기 위해 생성자가 파이썬 역시 있으며 이는 __init__() 멤버함수이다. 소멸자는  __del__() 이다. 생성자는 인스턴스 객체 생성시 호출되면 소멸자는 인스턴스 객체의 참조 카운트가 0이 될때 호출된다.
정적 메서드 : 원래 클래스의 멤버 함수를 사용하기 위해서는 인스턴스 객체를 생성하고 그것을 통해서 가능한데(그렇기 때문에 이러한 멤버 함수는 반드시 인자로 self가 있어야 한다) 인스턴스 객체 없이 클래스를 통해 직접 호출할수 있는 맴버 함수를 static method라고 한다(객체를 통하지 않기때문에 self 매개변수가 필요 없다).
클래스 메서드 : 클래스 메서드는 정적 메서드와 동일하나 단 암묵적으로 클래스를 첫 인자로 받는다.
파이썬에서 클래스는 기본적으로 public의 속성인데 멤버 변수 이름 앞에 __를 붙이면 private가 된다.
클래스의 연산자 오버로딩을 위한 멤버 함수들이 있다(책 참조).
인스턴스 객체의 멤버 변수 정보를 알려면 인스턴스 객체.__dict__을 이용하면 된다.
클래스 객체.__bases__ 는 클래스 객체의 직계 base 클래스를 알 수 있다.
파이썬에서는 base 클래스의 생성자를 이용하려면 derived 클래스의 생성자 내에 명시적으로 base 클래스를 언바운드 메서드 호출(self 인자를 넘겨주면서 호출하는 것)을 해야 한다.
C++에서는 method overriding 하기 위해서는 base 클래스의 함수와 derived 클래스의 함수가 이름, 매개변수, 리턴값이 같아야 하나 python에서는 함수 이름만 같아도 된다.
책에는 안나온 내용이지만 클래스 내에 함수 overloading은 python에서 안되는 것 같다. 그러나 *argv 나 **argv 나 기본적으로 python 자체가 각 자료형에 맞게 polymorphism이 이뤄진다는 걸생각하면 C++의 함수 overloading의 문법이 아니라도 의미적으로 가능하다.
클래스 객체.__mro__ 라고하면 상속 구조 속에 이름을 찾는 순서가 튜플로 나타내어진다.
다중 상속일 때 derived 클래스에서 base 클래스의 생성자를 호출할경우 중복 호출이 일어 날수 있는데 이러한 일을 방지 하기 위해 super() 내장함수를 이용한다(super().__init__(self)).

chapter6 : 모듈
모듈 이란? 여러코드를 한데 묶어 다른 곳에서 재사용 할수 있는 코드 모음(보통 비슷한 기능을 하는 함수나 큰 기능을 수행하는데 필요한 일련의 함수와 데이터가 포함)
dir(모듈) 하면 모듈 내의 함수, 데이터의 종류를 볼수 있다.
모듈의 정보 : http://docs.python.org/release/3.0.1/library/index.html
python의 모듈 경로 찾기 :  1.현재 디렉토리 2.PYTHONPATH 3.표준라이브러리디렉토리 , 위의 경로들은 sys.path에서볼수 있으며 추가도 가능하다.
import 문은 어디서나 가능, 심지어 함수 안에서 조차.
from <모듈> import * 하면 __로 시작하는 모듈안의 어트리뷰트 (함수와 데이터)를 제외한 모든 어트리뷰트를 이름 공간에 임포트 합니다.
모듈 reload를 위해 import imp; imp.reload(<모듈>).
모듈도 객체이다. 곧 모듈이 import가 되면 메모리에 로딩되면서 레퍼런스를 전달해 준다.
모듈의 __name__ 속성은 자신이 모듈로 쓰이면 모듈 이름이, 자신이 main 프로그램으로 직접 호출이 되면 __main__을 리턴한다.
관련있는 여러개의 모듈을 묶은 것을 패키지라고 한다. 패키지 디렉토리 안에는 여러 모듈 디렉토리와 함께 __init__.py 파일이 있는데 이는 패키지가 import 될때 실행되는 파일이다. __init__.py 파일 안에 __all__ 속성은 form import * 을 실행할때 포함할 하위 패키지의 목록이다. 상위 패키지를 import  했다고 해서 하위 패키지가 import 되는 것이 아니다. 하위 패키지의 모듈을 사용하기 위해서는 반드시 하위 패키지를 import 해야 한다.

chapter7 : 예외처리
프로그램의 제어 흐름을 조정하기 위해 사용하는 이벤트를 exception(예외)라고 한다.
이건 책 참조. 각종 내장 예외에 대한 것들.
try:
except:
finally:
raise 구문
assert 구문

chapter8 : 입출력
2.x 버젼에선 print는 함수가 아니였으나 3 버젼에서는 함수다.
locals(),vars(),globals() 함수는 지역변수들을 사전 형태로 반환한다.
open으로 파일 객체를 만들면 read(),readline(),readlines() 뿐만 아니라 seek()과 tell()함수도 제공된다.
seek() : 사용자가 원하는 위치로 파일 포인터를 이동
tell() :현재 파일에서 어디까지 읽고 썻는지 위치를 반환
with 구문을 이용하면 close()을 안해도 된다.
with open('test.txt') as f: print(f.readlines());print(f.cloased) 를 False 가 나오는데 위 구문을 마치고 f.closed로 확인해 보면 파일 객체인 f가 닫혀 있는걸 확인 할수 있다.
colors = ['red','green','black']이라는 변수가 있을때 이 내용을 그대로 파일에 넣고 나중에 읽었을때 그대로 리스트로 받을 수 있는데 이는 pickle이라는 모듈을 이용하면 된다.
import pickle; f= open('colors.txt','wb'); pickle.dump(colors,f); f.close(); del colors; f = open('colors.txt',rb); colors=pickle.load(f); f.close() 이렇게 하면 리스트 내용이 바이너리로 colors.txt 파일에 들어갔다가 고대로 pickle.load()로 읽어 들이면 바로 리스트로 입력이 된다(pickle을 이용할땐 반드시 바이너리 형식을 이용해야 한다). 이러한 pickle의 대상은 파이썬의 대부분의 객체가 가능하다(기본 자료형, 사용자 정의 클래스). 다만 pickle.dump로 사용자 정의 클래스를 파일에 바이너리로 저장했다가 다시 load로 읽어 들일때 그 클래스가 프로그램 내에 사전에 정의 되어 있어야 한다. 그렇지 않으면 AttributeError 발생

chapter9 : C/C++와 연동

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함수를 한번만 호출하기 때문에 성능이 탁월하다.