참고 도서 |
[그림] PyCrust 셸의 최초 모습
[그림] "dna"객체에 사용할 수 있는 메소들을 보여주는 팝업 리스트
>>> dna = "CTGACCACTTTACGAGGTTAGC"그리고 나서 단순히 그 변수의 이름을 타이핑해 넣으면 파이썬이 그 변수의 값을 화면에 표시함으로써 응답한다. 그 값을 인용부호로 둘러싸서 그것이 문자열이라는 것을 상기시켜준다.
>>> dna "CTGACCACTTTACGAGGTTAGC"파이썬 문자열은 여러 내장 능력을 구비하고 있다. 그 중에 한 능력은 자신의 복사본을 모두 소문자로 바꾸어 반환하는 능력이다. 이런 능력들을 사람들은 메소드라고 알고 있다. 한 객체의 메소드를 요청하려면, 점 구문을 사용하자. 다시 말해 (이 경우에는 한 문자열 객체에 대한 참조점인) 그 변수의 이름을 타자하고 다음에 점 연산자(.)를 그 다음에 메소드의 이름을 타자한다. 그 다음에 열기괄호와 닫기괄호를 타자하라.
>>> dna.lower() "ctgaccactttacgaggttagc"지표화 연산자 s[i]를 사용하면 문자열의 일부에 접근할 수 있다. 지표화는 0에서 시작하며, 그래서 s[0]은 그 문자열에서 첫 번째 문자를 반환하고 s[1]은 두 번째 문자를 반환한다. 등등.
>>> dna[0] "C" >>> dna[1] "T" >>> dna[2] "G" >>> dna[3] "A"여기에 나온 스크린샷에서 마지막 줄은 PyCrust가 구비한 자동완성의 특징을 보여주는데, 한 객체 변수 뒤에 점이 타자되면 한 객체에서 유효한 메소드와 (그 특성들)의 목록이 화면에 표시된다. 보시다시피, 파이썬의 리스트가 가진 많은 내장 능력들을 파이썬 셸에서 시험해 볼 수 있다. 이제 다른 파이썬 연속열 형으로 리스트를 살펴 보자.
>>> bases = ["A", "C", "G", "T"] >>> bases ["A", "C", "G", "T"] >>> bases.append("U") >>> bases ["A", "C", "G", "T", "U"] >>> bases.reverse() >>> bases ["U", "T", "G", "C", "A"] >>> bases[0] "U" >>> bases[1] "T" >>> bases.remove("U") >>> bases ["T", "G", "C", "A"] >>> bases.sort() >>> bases ["A", "C", "G", "T"]이 예제에서는 우리가 베이스(bases)라고 부르는 한 개 짜리 문자열로 구성된 리스트를 하나 만들었다. 그리고나서 한 요소를 그 끝에 추가했고, 모든 요소들의 순서를 역으로 바꾸었으며, 지표 위치로 요소들을 열람하였다. 그리고 "U"라는 값을 가진 요소를 제거하였다. 리스트에서 요소 하나를 제거하려면 추가 정보를, 즉 리스트에서 제거하고자 하는 값을 remove() 메소드에 공급할 필요가 있다. 아래의 그림에서 볼 수 있듯이, PyCrust는 대부분의 연산에서 필수적인 것들을 알려주는 파이썬의 능력을 활용하여 호출 팁 팝업 창에서 그 정보를 보여준다.
[그림] "remove" 메소드의 사용법을 보여주는 툴팁
>>> dna = "CTGACCACTTTACGAGGTTAGC" >>> bases = ["A", "C", "G", "T"] >>> len(dna) 22 >>> len(bases) 4 >>> dir(dna) ["__add__", "__class__", "__contains__", "__delattr__", "__doc__", "__eq__", "__ge__", "__getattribute__", "__getitem__", "__getslice__", "__gt__", "__hash__", "__init__", "__le__", "__len__", "__lt__", "__mul__", "__ne__", "__new__", "__reduce__", "__repr__", "__rmul__", "__setattr__", "__str__", "capitalize", "center", "count", "decode", "encode", "endswith", "expandtabs", "find", "index", "isalnum", "isalpha", "isdigit", "islower", "isspace", "istitle", "isupper", "join", "ljust", "lower", "lstrip", "replace", "rfind", "rindex", "rjust", "rstrip", "split", "splitlines", "startswith", "strip", "swapcase", "title", "translate", "upper"] >>> dir(bases) ["__add__", "__class__", "__contains__", "__delattr__", "__delitem__", "__delslice__", "__doc__", "__eq__", "__ge__", "__getattribute__", "__getitem__", "__getslice__", "__gt__", "__hash__", "__iadd__", "__imul__", "__init__", "__le__", "__len__", "__lt__", "__mul__", "__ne__", "__new__", "__reduce__", "__repr__", "__rmul__", "__setattr__", "__setitem__", "__setslice__", "__str__", "append", "count", "extend", "index", "insert", "pop", "remove", "reverse", "sort"] >>> list(dna) ["C", "T", "G", "A", "C", "C", "A", "C", "T", "T", "T", "A", "C", "G", "A", "G", "G", "T", "T", "A", "G", "C"]이제부터는 생물학적 연속열 데이터와 관련된 유용한 연산을 수행할 함수들을 정의하겠다.
>>> def transcribe(dna): ... """dna 문자열을 rna 문자열로 반환하라.""" ... return dna.replace("T", "U") ... >>> transcribe("CCGGAAGAGCTTACTTAG") "CCGGAAGAGCUUACUUAG"이 예제에서 transcribe라고 부르는 함수 하나를 만들었는데 이 함수는 건네지는 문자열이 DNA 배열을 표현한다고 예상한다. 문자열은 replace() 메소드를 가지고 있는데 이 메소드는 한 문자가 출현할 때마다 다른 문자로 바꾸어서 원래 문자열의 복사본을 반환할 것이다. 한 줄의 DNA를 RNA로 일관되게 전사하는 방법을 3줄의 코드로 만들었다. 또다른 함수를 만들어 보자. reverse는 어떤가?
>>> def reverse(s): ... """서열 문자열을 순서를 거꾸로 하여 반환하라.""" ... letters = list(s) ... letters.reverse() ... return "".join(letters) ... >>> reverse("CCGGAAGAGCTTACTTAG") "GATTCATTCGAGAAGGCC"이 함수에는 새로 설명이 필요한 것들이 몇 개 있다. 먼저, 인수 이름을 "dna" 대신에 "s"라고 사용했다. 파이썬에서는 아무거나 마음에 드는 이름으로 인수를 명명해도 좋다. 예상된 값이나 의도에 근거하여 짧은 이름을 사용하는 것이 관례라면 관례이다. 그래서 문자열에 대하여 "s"를 사용하는 것은 파이썬 코드에서 아주 흔한 일이다. 이 예제에서 "dna" 대신에 "s"를 사용한 또다른 이유는 이 함수가 단순히 dna 배열을 표현한 문자열 뿐만 아니라 어느 문자열에도 올바로 작동하기 때문이다. 그래서 "s"가 "dna"보다 이 함수의 포괄적인 쓰임새를 더 잘 반영했다고 할 수 있다.
>>> basecomplement = {"A": "T", "C": "G", "T": "A", "G": "C"} >>> basecomplement.keys() ["A", "C", "T", "G"] >>> basecomplement.values() ["T", "G", "A", "C"] >>> basecomplement["A"] "T" >>> basecomplement["C"] "G" >>> for base in basecomplement.keys(): ... print "The complement of", base, "is", basecomplement[base] ... The complement of A is T The complement of C is G The complement of T is A The complement of G is C >>> for base in basecomplement: ... print "The complement of", base, "is", basecomplement[base] ... The complement of A is T The complement of C is G The complement of T is A The complement of G is C이 예제에서는 for 루프의 개념도 소개되고 있는데, 이는 basecomplement 사전의 키들을 순회한다. 파이썬의 for 루프는 어느 연속열도 반복할 수 있다. 이 예제에서는 keys()가 반환한 리스트의 첫 번째 값을 base라는 이름의 변수에 할당하고, print 서술문을 실행한다. 그리고나서 그 리스트에 있는 각 하부연속 값들에 대하여 그 과정을 반복한다. 두 번째 for 루프의 예에서, 단순히 "for base in basecomplement"라고 지정하면 파이썬은 기본으로 basecomplement 사전의 키들을 루프 하는 것을 볼 수 있다.
>>> letters = list("CCGGAAGAGCTTACTTAG") >>> [basecomplement[base] for base in letters] ["G", "G", "C", "C", "T", "T", "C", "T", "C", "G", "A", "A", "T", "G", "A", "A", "T", "C"]리스트 내포는 리스트를 반환하고 for 루프와 비슷하게 작동하지만, 훨씬 더 간결하고 효율적인 형식이다. 이 경우에 리스트 내포를 사용하면 원래의 문자 리스트에서 각 베이스가 그 짝으로 대체된 새로운 리스트를 반환할 수 있다. 짝은 basecomplement 사전에서 열람하였다. 어떻게 이 모든 것을 하나로 합치는지 살펴보자.
>>> def complement(s): ... """짝 서열 문자열을 반환하라.""" ... basecomplement = {"A": "T", "C": "G", "G": "C", "T": "A"} ... letters = list(s) ... letters = [basecomplement[base] for base in letters] ... return "".join(letters) ... >>> complement("CCGGAAGAGCTTACTTAG") "GGCCTTCTCGAATGAATC"이제 reverse 함수와 complement 함수가 있으므로, reversecomplement 함수를 위한 빌딩블록을 갖추었다.
>>> def reversecomplement(s): ... """dna 문자열의 반대 짝(reverse complement)을 반환하라.""" ... s = reverse(s) ... s = complement(s) ... return s ... >>> reversecomplement("CCGGAAGAGCTTACTTAG") "CTAAGTAAGCTCTTCCGG"다음 코드는 G와 C 베이스로 구성된 DNA 비율을 아는 데 쓸모가 있을 것이다. 문자열 객체는 count() 메소드를 가지는데 이 메소드는 문자열의 출현 빈도를 반환한다. 그 정보를 가지고 그 비율을 계산하는 것은 간단한 수학적 계산을 하는 것 만큼이나 쉬운 일이다.
>>> def gc(s): ... """G+C로 구성된 dna 비율을 반환하라.""" ... gc = s.count("G") + s.count("C") ... return gc * 100.0 / len(s) ... >>> gc("CCGGAAGAGCTTACTTAG") 50.0 >>> gc("CCGGAAGAGCTTACTTAGTTA") 42.857142857142854DNA는 3개의 문자 부분(코돈)으로 분할될 수 있기 때문에, 코돈 리스트를 반환하는 함수가 쓸모가 있을 것이다. DNA 문자열이 3개로 나누어 떨어지지 않는 경우에 또다른 간단한 수학적 계산으로 코돈의 마지막 점을 결정한다. range() 함수는 첫 점에서 마지막 점까지 이 경우에는 3씩 그 값을 증가하시키면서 숫자 리스트를 반환한다. 이런 수치계산적 진행은 문자열 슬라이싱과 함께 리스트 내포안에서 사용되어 3개의 문자열을 가진 리스트를 만들어 낸다.
>>> def codons(s): ... """dna 문자열에 대하여 코돈 리스트를 반환하라.""" ... end = len(s) - (len(s) % 3) - 1 ... codons = [s[i:i+3] for i in range(0, end, 3)] ... return codons ... >>> codons("CCGGAAGAGCTTACTTAG") ["CCG", "GAA", "GAG", "CTT", "ACT", "TAG"]문자열 슬라이싱(slicing)은 문자열 지표화(indexing)와 비슷하다. 문자열 하나를 열람하는 대신에, 문자열 슬라이싱은 시작위치에서부터 마지막 점 미만까지 한 구역의 문자열을 열람할 수 있도록 해준다. 그 구문은 s[i:j]인데, 여기에서 i는 시작 위치이고 j는 끝 위치이다. 그래서 s[0:3]은 지표 0, 1, 그리고 2에 위치한 문자열 하나를 반환한다.
>>> s = "CCGGAAGAGCTTACTTAG" >>> s[0:3] "CCG" >>> s[3:6] "GAA" >>> s[6:9] "GAG" >>> s[9:12] "CTT"함수에 대해 마지막으로 흥미로운 사실 하나를 주목하자. 함수는 그 자체로 객체이다. 이는 곧 문자열과 리스트에 하던 것과 똑같이, 그의 속성들을 dir()을 사용하여 들여다 볼 수 있다는 뜻이다. 더 유용한 함수 객체의 속성 하나는 문서화 문자열이다. 이 문자열은 __doc__ 특성에 저장되어 있다.
>>> dir(transcribe) ["__call__", "__class__", "__delattr__", "__dict__", "__doc__", "__get__", "__getattribute__", "__hash__", "__init__", "__name__", "__new__", "__reduce__", "__repr__", "__setattr__", "__str__", "func_closure", "func_code", "func_defaults", "func_dict", "func_doc", "func_globals", "func_name"] >>> transcribe.__doc__ "Return dna string as rna string."이 마지막 예제가 이해가 안가더라도 걱정하지 말자. 이 예제를 보여주는 주요 의도는 파이썬이 아주 강력하고 일관성이 있다는 것을 강조하기 위해서였다. 파이썬에서 모든 것은 객체이며, 객체들의 내부를 바로바로 들여다 볼 수 있다는 것을 보여주기 위함이었다. 결과적으로 여러분은 파이썬을 익혀감에 따라, 처음에는 친숙하지 않던 객체가 맨 처음 사용할 때 예상하곤 했던 행동과 종종 똑같이 행동한다는 것을 알게 될 것이다. 이는 다른 프로그래밍 언어를 사용할 때는 경험하기 힘든 강렬한 느낌이다.
class DNA: """문자열 연속열로 DNA를 표현하는 클래스.""" basecomplement = {"A": "T", "C": "G", "T": "A", "G": "C"} def __init__(self, s): """DNA 실체를 문자열 s로 초기화하여 만들어라.""" self.seq = s def transcribe(self): """rna 문자열로 반환하라.""" return self.seq.replace("T", "U") def reverse(self): """dna 문자열을 역순서로 반환하라.""" letters = list(self.seq) letters.reverse() return "".join(letters) def complement(self): """짝 dna 문자열을 반환하라.""" letters = list(self.seq) letters = [self.basecomplement[base] for base in letters] return "".join(letters) def reversecomplement(self): """dna 문자열의 반대 짝(reverse complement)을 반환하라.""" letters = list(self.seq) letters.reverse() letters = [self.basecomplement[base] for base in letters] return "".join(letters) def gc(self): """G+C로 구성된 dna 비율을 반환하라.""" s = self.seq gc = s.count("G") + s.count("C") return gc * 100.0 / len(s) def codons(self): """dna 문자열에 대하여 코돈(codons) 리스트를 반환한다.""" s = self.seq end = len(s) - (len(s) % 3) - 1 codons = [s[i:i+3] for i in range(0, end, 3)] return codons이 중 상당수는 기존의 함수에 기반을 두고 있기 때문에 익숙할 것이다. 클래스 정의에는 다루어 보아야 할 새로운 요소들 몇 개가 추가된다. 나머지를 더 자세하게 탐험하기 전에 이 새 클래스를 사용하는 법을 살펴보자.
>>> from bio import DNA >>> dna1 = DNA("CGACAAGGATTAGTAGTTTAC") >>> dna1.transcribe() "CGACAAGGAUUAGUAGUUUAC" >>> dna1.reverse() "CATTTGATGATTAGGAACAGC" >>> dna1.complement() "GCTGTTCCTAATCATCAAATG" >>> dna1.reversecomplement() "GTAAACTACTAATCCTTGTCG" >>> dna1.gc() 38.095238095238095 >>> dna1.codons() ["CGA", "CAA", "GGA", "TTA", "GTA", "GTT", "TAC"]클래스는 여러 객체 실체들을 만드는 데 사용되었던 일종의 틀처럼 행동하기 때문에, 클래스 메소드 내부에 메소드가 호출될 특정 객체 실체를 참조할 능력이 필요하다. 이런 필요성을 고려하여 파이썬은 자동으로 객체 실체를 각 메소드에 첫 번째 인자로 건넨다. 파이썬 공동체에서의 관례는 그 첫 인자를 "self"라고 이름 짓는 것이다. 그 때문에 DNA 클래스의 메소드 정의에서 첫 번째 인자가 모두 "self"이다.
>>> dna2 = DNA("ACGGGAGGACGGGAAAATTACTAGCACCCGCATAGACTT") >>> dna2.codons() ["ACG", "GGA", "GGA", "CGG", "GAA", "AAT", "TAC", "TAG", "CAC", "CCG", "CAT", "AGA", "CTT"] >>> dna3 = DNA(dna1.seq + dna2.seq) >>> dna3.reversecomplement() "AAGTCTATGCGGGTGCTAGTAATTTTCCCGTCCTCCCGTGTAAACTACTAATCCTTGTCG" >>> dna4 = DNA(dna3.reversecomplement()) >>> dna4.codons() ["AAG", "TCT", "ATG", "CGG", "GTG", "CTA", "GTA", "ATT", "TTC", "CCG", "TCC", "TCC", "CGT", "GTA", "AAC", "TAC", "TAA", "TCC", "TTG", "TCG"]이토록 초보적인 클래스 정의를 가지고도 파이썬 셸에서 조작을 하면, 생물학적 데이터를 최소한의 구문적 부담으로 일관성 있고 깨끗하게 분석하는 파이썬의 잠재적인 능력을 볼 수 있을 것이다.
이전 글 : 코코아 프로그램 만들어보기
다음 글 : DOM과 SAX의 명복을 빌면서
최신 콘텐츠