目錄html
4.1序列數據的表徵python
4.2 存儲 DNA 序列的程序程序員
4.3 鏈接DNA片斷算法
4.4 轉錄:從 DNA 到 RNAshell
4.5 使用Python文檔數據庫
4.6 在Python中計算反向互補編程
4.7 蛋白質、文件和數組小程序
4.8 從文件中讀取蛋白質序列數據python3.x
4.9 列表數組
4.10 練習題
在本章中,咱們將開始編寫Python程序,來處理DNA和蛋白質的生物序列數據。在你的計算機上,一旦有了這樣的序列,你就能夠編寫程序對序列數據進行下列的處理了:
此外,你還將編寫獲取序列信息的程序。DNA的GC含量如何?蛋白質的疏水性如何?你將學習相關的編程技術,利用它們就能夠來回答這些相似的問題了。
在本章中你將學習到的技能涉及Python語言的基礎,此處列出了其中的一部分:
本書的大部份內容都是處理表徵DNA和蛋白質生物學序列的符號。在生物信息學中,使用這些符號來表徵序列,這些符號和生物學家平常工做中用來表徵序列的符號是徹底同樣的。
如前所述,DNA是由四種基礎分子組成的,它們就是核酸,也叫作核苷酸或鹼基;而蛋白質則是由20中基礎分子組成的,它們就是氨基酸,也叫作殘基。蛋白質的片斷叫作肽(即縮氨酸)。無論是DNA仍是蛋白質,本質上都是多聚物,由構成它們的基礎分子首尾相連而造成。因此,僅僅經過鹼基或氨基酸序列就能夠表徵DNA或蛋白質的基本結構。
這些都是最基本的定義。我假設你對這些內容都比較熟悉,或者打算查閱一本入⻔性的參考書來學習更加詳細的內容。表4.1中列出的是鹼基;在鹼基上加上一個糖基,就能夠獲得核苷:腺苷、⻦苷、胞苷、胸苷和尿苷;在覈苷上再加一個磷酸基團,就能夠獲得核苷酸:腺苷酸、⻦苷酸、胞苷酸、胸苷酸和尿苷酸。核酸就是由核苷酸經過化學鍵相連造成的序列。肽是由數個氨基酸相連而成的,更⻓的話就叫作多肽了。蛋白質是由一個或多個多肽組成的生物學功能基團。殘基指的就是多肽鏈中的氨基酸。爲了方便,如表4.1和表4.2所示,經常使用一個字⺟或三個字⺟的代碼來表示核酸和氨基酸。(本書中主要用單字⺟代碼來表示氨基酸。)
代碼 |
核酸 |
A |
腺嘌呤 |
C |
胞嘧啶 |
G |
⻦嘌呤 |
T |
胸腺嘧啶 |
U |
尿嘧啶 |
M |
A 或 C (aMino, 氨基) |
R |
A 或 G (puRine, 嘌呤) |
W |
A 或 T (Weak, 做用力弱) |
S |
C 或 G (Strong, 做用力強) |
Y |
C 或 T (pYrimidine, 嘧啶) |
K |
G 或 T (keto, 酮基) |
V |
A 或 C 或 G(非 T) |
H |
A 或 C 或 T(非 G) |
D |
A 或 G 或 T(非 C) |
B |
C 或 G 或 T(非 A) |
N |
A 或 G 或 C 或 T (aNy, 任何一個鹼基) |
表 4.1中的核酸代碼不只包括四種基本核酸的字⺟縮寫,還定義了二個核酸、三個核酸或四個核酸全部可能組合的單字⺟縮寫。在本書的大多數例子中,我僅使用 A、C、G、T、U 和 N。A、C、G 和 T 字⺟表明了 DNA 的核酸,而當 DNA(脫氧核糖核酸)轉錄成RNA(核糖核酸)時 U 將替換 T。當測序儀沒法肯定鹼基時,經常使用 N 來表示「未知鹼基」。稍後,在第 9 章中,在編程處理限制性酶切圖譜時,咱們還須要用到表示各類核酸組合的其餘代碼。有時,也會使用這些單字⺟代碼的小寫形式,這對 DNA 來講比較常⻅,但在蛋白質中則不多這樣使用。
單字⺟代碼 |
氨基酸 |
三字⺟代碼 |
A |
Alanine, 丙氨酸 |
Ala |
B |
Aspartic acid or Asparagine, 天冬氨酸 或天冬醯胺 |
Asx |
C |
Cysteine, 半胱氨酸 |
Cys |
D |
Aspartic acid, 天冬氨酸 |
Asp |
E |
Glutamic acid, 穀氨酸 |
Glu |
F |
Phenylalanine, 苯丙氨酸 |
Phe |
G |
Glycine, 甘氨酸 |
Gly |
H |
Histidine, 組氨酸 |
His |
I |
Isoleucine, 異亮氨酸 |
Ile |
K |
Lysine, 賴氨酸 |
Lys |
L |
Leucine, 亮氨酸 |
Leu |
M |
Methionine, 甲硫氨酸 |
Met |
N |
Asparagine, 天冬醯胺 |
Asn |
P |
Proline, 脯氨酸 |
Pro |
Q |
Glutamine, 谷氨醯胺 |
Gln |
R |
Arginine, 精氨酸 |
Arg |
S |
Serine, 絲氨酸 |
Ser |
T |
Threonine, 蘇氨酸 |
Thr |
V |
Valine, 纈氨酸 |
Val |
W |
Tryptophan, 色氨酸 |
Trp |
X |
Unknown, 未知氨基酸 |
Xxx |
Y |
Tyrosine, 酪氨酸 |
Tyr |
Z |
Glutamic acid or Glutamine, 穀氨酸或谷氨醯胺 |
Glx |
對於表 4.1和表 4.2中的代碼來講,計算機科學中的術語和生物學中的術語仍是有必定差異的。從計算機科學的⻆度來看,這兩個表定義了兩個按字⺟順序排列的有限的符號集合,使用它們能夠來構建字符串。字符串指的就是符號序列。好比,這句話自己就是一個字符串(this sentence is a string)。語言就是一個(有限或無限)字符串的集合。在本書中,語言主要就是 DNA 和蛋白質的序列數據。和在序列數據中的具備生物學含義的表徵不一樣,生物信息學家經常會把真正的 DNA 或蛋白質序列稱做「字符串」。兩個不一樣學科中的術語會交叉使用,這就是一個例子。
如同你在表格中看到的那樣,咱們會使用簡單的字⺟來表徵數據,這和在紙張上手寫時使用的方式是同樣的。計算機實際上會用另外的代碼來表徵簡單的字⺟,但你不用擔憂這些,只要記住在使用文本編輯器時將它們保存爲 ASCII 或者純文本便可。
ASCII 是計算機在內存中存儲文本(和控制信息)數據的一種方式。當文本編輯器等程序讀取數據時,計算機知道它正在讀取 ASCII,由於計算機知道每一個代碼表明什麼,因此它就會在屏幕上以一種容易識別的方式把相應的⺟顯示出來。總而言之,知道 ASCII 是計算機表徵文本的一種代碼就足夠了。
讓咱們來寫一個小程序吧,它把 DNA 存儲在變量中,而後把它打印輸出到屏幕上。
咱們會用最經常使用的方式——由 A、C、G 和 T 四個字⺟組成的字符串——來書寫 DNA,而且把存儲 DNA 的變量叫作 DNA。換言之,DNA 就是程序中 DNA 序列數據的名稱。注意這一點,在 Python 中,變量就是你打算處理的數據的名稱,使用該名稱,你能夠對數據進行徹底的訪問。例 4.1是完整的程序。
#!/usr/bin/env python # Storing DNA in a variable, and printing it out # First we store the DNA in a variable called DNA DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' # Next, we print the DNA onto the screen print(DNA) # Finally, we'll specifically tell the program to exit. exit()
在第 2 章中,咱們已經學習了文本編輯器和運行 Python 程序的知識,運用這些知識,輸入例子中的代碼(或者從書籍官網上把它複製下來)並保存到一個文件中。牢記必定要把程序保存成 ASCII 或者純文本格式,不然 Python 在讀取該文件時可能會出現問題。
接下來就是運行程序了。運行程序的具體步驟取決於你使用的計算機(參看第 2 章)。
咱們假定程序是你計算機中一個叫作 example4-1 的文件。回顧第 2 章中相關的知識,若是你想在 Unix、Linux 或者 Mac 中運行這個程序,須要在 shell 窗口中鍵入:
python example4-1
在 Windows 中,在 MS-DOS 命令窗口中鍵入:
python example4-1
若是你成功運行了該程序,在你的計算機屏幕上你將看到相應的輸出。
例 4.1展現了全部 Python 程序都將用到的許多理念,其中一個即是控制流的理念,即計算機是以什麼順序來執行程序中的語句的。
全部的程序都從第一行開始,除非明確指明瞭其餘的運行順序,不然它將一條一條地執行語句,直到程序的最後一行。例 4.1只是簡單的從頭至尾執行程序,並無其餘的運行支路。
在後續的章節中,你將學習到如何編程控制程序的執行順序。
如今讓咱們看一下例 4.1程序中的細節。你會發現其中有許多空行,它們的存在使得程序更容易被人閱讀。另外,注意以 # 起始的註釋。在第 3 章中提到過,當 Python 運行時,它會把這些註釋和空行所有忽略掉。事實上,對於 Python 來講,下面的程序和例 4.1中的程序是徹底同樣的:
#!/usr/bin/python DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC'; print(DNA); exit();
在例 4.1中,我使用了大量的註釋。代碼開始的註釋指明瞭程序的用途、做者以及其餘信息,當其餘人須要理解代碼時,這些信息會對他有很大的幫助。註釋還解釋了代碼每一部分的做用,有時還對代碼的工做原理進行了闡釋。
瞭解註釋的重要性是很是必要的。在大多數高校的計算機科學課程的課堂做業中,沒有註釋的程序一般會獲得很低的分數甚至不及格;而在工做中不對代碼進行註釋的程序員,他的職業生涯將是短暫而失敗的。
程序的第一行以 # 起始,這使得它看上去像是一行註釋,但又不像是有什麼信息含量的註釋:
#!/usr/bin/python
這是比較特殊的一行,叫作命令解釋,它告訴運行 Unix 或者 Linux 的計算機,這是一個 Python 程序。在不一樣的計算機中,這一行可能會有些許的差別。在某些計算機中,這一行並非必需的,由於計算機能夠經過其餘信息識別出 Python。在 Windows 計算機中,一般會配置成把以.py 結尾的任何程序都假定成 Python 程序。在 Unix 或 Linux 中、Windows 的命令窗口中、或者 MacOS X 的 shell 中,你能夠鍵入 python my_program,這樣你的 Python 程序 my_program 中就不須要這樣特殊的一行了。然而,一般都會寫上這一行,因此在咱們全部程序的開頭也都會有它。
例 4.1中的下一行代碼把 DNA 存儲到了一個變量中:
DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC'
這在計算機語言中是很是常⻅、很是重要的,因此咱們將對它進行詳細的解釋。總的來講,你將會看到 Python 和編程語言的一些基本特性,因此不要跳讀了,停下來慢慢閱讀學習吧。
這行代碼叫作語句。在 Python 中,語句以換行結尾。
更準確的來講,這一行是一個賦值語句。在該程序中,它做用就是把 DNA 存儲到DNA 變量中。正如在接下來的小節中你將看到的那樣,此處有許多基本的事件發生。
首先,讓咱們來看看 DNA 變量。它的名字有些隨意,你能夠給它起另一個名字,程序仍會正常運行。舉個例子,若是你把下面這兩行:
DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' print(DNA)
換成這樣的兩行:
A_poem_by_Seamus_Heaney = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' print(A_poem_by_Seamus_Heaney)
程序仍將像原來那樣正常運行,把 DNA 打印輸出到計算機屏幕上。無論怎樣,計算機程序中變量的名字都是由你來起的。(要知足特定的限制:在 Python 中,變量的名字只能由大小寫字⺟、數字和下劃線 _ 組成,並且第一個字符不能是數字。)
前面已經強調過,使用空行和註釋可使代碼更加易讀,變量的命名也存在一樣的問題。無論變量名是 DNA 仍是 A_poem_by_Seamus_Heaney,對於計算機來講都沒有什麼特殊的含義,但對於閱讀程序的人來講就不同了。有意義的變量名,能夠清晰地代表程序中變量的做用,使得程序更加容易理解。其餘的名字可能會使得程序的功能和變量的做用不甚明朗。使用精心選擇的變量名是自文檔化代碼的一部分(self-documenting code)。精心選擇變量名的話,你仍然須要註釋,但就不須要那麼多的註釋了。
你會注意到 DNA 變量名以一個美圓符號起始。在 Python 中,這樣的變量叫作標量變量,它是存儲單個數據項目的變量。在存儲字符串或者各類各樣的數字(如,字符串 hello,或者 2五、6.23四、3.5E十、-0.8373 這樣的數字)時,須要使用標量變量。一個標量變量一次只能存儲數據中的一個項目。
在例 4.1中,標量變量 $DNA 存儲着用 A、C、G 和 T 表徵的 DNA。如前所述,在計算機科學中,字⺟序列叫作字符串。在 Python 中,你須要把它放在引號中來代表它是字符串。可使用單引號,就像例 4.1中那樣,也可使用雙引號。(稍後你會看到二者的區別。)所以,DNA 就被表徵成:
'ACGGGAGGACGGGAAAATTACTACGGCATTAGC'
賦值
在 Python 中,要把一個變量設成特定的值,須要使用 = 符號。= 符號所以被叫作賦值操做符。在例 4.1中,值
'ACGGGAGGACGGGAAAATTACTACGGCATTAGC'
被賦給了 DNA 變量。賦值後,你能夠經過變量名來獲取它的值,就像例 4.1中的 print 語句那樣。
在賦值語句中,每一個部分的順序是很是重要的。要賦給變量的值在賦值操做符的右邊,而須要賦值的變量總在賦值操做符的左邊。在編程手冊中,有時你可能會遇到 lvalue 和 rvalue 這樣的術語,它們分別指代賦值操做符左邊和右邊的項目。在編程語言中,使用 = 符號進行賦值有一段很⻓的歷史。然而,這也致使了某種形式的混亂:好比說,在數學中,使用 = 時表示等號兩邊的數是相等的。因此,必定要牢記,在 Python 中,= 符號並不表示相等,而是把值賦給一個變量。(稍後咱們會看到如何表示相等)。
來總結一下,對於這個語句,咱們都學習到了那些知識:
DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC'
這是一個賦值語句,它把表徵 DNA 的字符串賦給了變量 DNA,做爲這個變量的值。
該語句:
print(DNA)
把 ACGGGAGGACGGGAAAATTACTACGGCATTAGC 打印輸出到計算機屏幕上。注意,print 語句處理的是標量變量,它把它的值打印輸出出來,此處就是變量 DNA 包含的字符串。
稍後,你將看到更多關於打印輸出的內容。
最後,exit() 語句告訴計算機退出程序。在 Python 語言中,在程序的最後並不須要 exit() 語句,一旦運行到結尾,程序就會自動退出。但放上這麼一個語句也沒什麼壞處,還明確表示了程序的結束。你會看到,在正常結束以前,程序會由於某些錯誤而退出,因此 exit() 語句仍是很是有用的。
如今,咱們對例 4.1進行簡單的修改,來演示一下如何把 DNA 片斷鏈接起來。所謂鏈接指的就是把一個東西附加在另外一個東西的結尾上。生物學家都知道,在生物學實驗室中把 DNA 序列鏈接起來是很是常⻅的工做,好比把克隆插入到細胞載體中,或者在基因表達過程當中把剪切的外顯子鏈接起來。許多生物信息學的軟件包都可以進行這樣的工做,所以這裏只是把它做爲一個實例來說解。例 4.2演示了關於字符串、變量和打印輸出語句的更多內容。
#!/usr/bin/env python # Concatenating DNA # Store two DNA fragments into two variables called DNA1 and DNA2 DNA1 = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' DNA2 = 'ATAGTGCCGTGAGAGTGATGTAGTA' # Print the DNA onto the screen print("Here are the original two DNA fragments:\n\n") print(DNA1, "\n") print(DNA2, "\n\n") # Concatenate the DNA fragments into a third variable and print them # Using "% format" DNA3 = "%s%s" % (DNA1, DNA2) print("Here is the concatenation of the first two fragments (version 1):\n\n") print("%s\n\n" % DNA3) # An alternative way using the "add operator": # Concatenate the DNA fragments into a third variable and print them DNA3 = DNA1 + DNA2 print("Here is the concatenation of the first two fragments (version 2):\n\n") print("%s\n\n" % DNA3) # Print the same thing without using the variable DNA3 print("Here is the concatenation of the first two fragments (version 3):\n\n") print(DNA1, DNA2, "\n", sep='') exit()
就像你看到的那樣,這裏有三個變量:DNA一、DNA2 和 DNA3。爲了對運行過程進行說明,我添加了一些 print 語句,這樣打印到計算機屏幕上的輸出就不只僅是一個接一個 DNA 片斷了,看起來會更加明瞭一些。下面是例 4.2的輸出:
Here are the original two DNA fragments: ACGGGAGGACGGGAAAATTACTACGGCATTAGC ATAGTGCCGTGAGAGTGATGTAGTA Here is the concatenation of the first two fragments (version 1): ACGGGAGGACGGGAAAATTACTACGGCATTAGCATAGTGCCGTGAGAGTGATGTAGTA Here is the concatenation of the first two fragments (version 2): ACGGGAGGACGGGAAAATTACTACGGCATTAGCATAGTGCCGTGAGAGTGATGTAGTA Here is the concatenation of the first two fragments (version 3): ACGGGAGGACGGGAAAATTACTACGGCATTAGCATAGTGCCGTGAGAGTGATGTAGTA
例 4.2和例 4.1有許多類似之處。讓咱們來看一下二者的不一樣吧。做爲開始,咱們會發現 print 函數語句中多了一些看起來並不直觀的東西:
print(DNA1) print(DNA1, DNA2, "\n", sep='')
像前述同樣,print 語句中有包含 DNA 的變量,但如今後面又多了逗號和 "\n",關鍵字參數「sep=''」。\n」是換行符,告訴計算機繼續下一行的開頭進行後續打印。關鍵字參數「sep=''」告訴每一個打印對象(DNA一、DNA2)之間使用無縫鏈接,默認是一個空格。
看一下例 4.2的代碼,確保已經明白這些換行符是如何決定輸出效果的。空行指的就是沒有任何打印輸出的一行。取決於你的操做系統,它可能僅僅是一個換行符,也多是換⻚符和回⻋符的組合(在這種狀況下,它也被叫作空白行),還有可能包含空格和製表符等非打印的空白字符。注意在 print 語句中還有逗號。逗號分隔列表中的項目,print 語句會打印輸出列表中的全部項目。僅此而已。如今,讓咱們看一下把 DNA1 和 DNA2 兩個 DNA 片斷鏈接到 DNA3 變量中的語句吧:
DNA3 = "%s%s" % (DNA1, DNA2)
對 DNA3 進行的賦值是一個典型的賦值操做,就像你在例 4.1看到的那樣,變量名後面跟着 = 符號,= 後則是要被賦予的值。
賦值語句中右邊的值是包裹在雙引號中的字符串。雙引號會把字符串中的%s替換爲變量的值,這叫作字符串格式化。因此事實上,此處的字符串就是 DNA1 變量中的 DNA,後面緊跟着 DNA2 變量中的 DNA。兩個 DNA 片斷鏈接後就被賦值給了變量 DNA3。
在把鏈接結果賦值給 DNA3 變量後,你把它打印出來,後面跟着一個空行:
print("%s\n\n" % DNA3)
程序的下面一部分就演示了鏈接兩個字符串的另一種辦法——使用加法操做符。當把加法操做符放在兩個字符串中間時,它會把原來的兩個字符串鏈接起來,產生一個新的字符串。因此這一行,演示了加法操做符的使用。
DNA3 = DNA1 + DNA2
最後,來練習一下 Python 的另一種方法,僅僅使用 print 語句就能夠完成一樣的鏈接任務:
print(DNA1, DNA2, "\n", sep='')
此處的 print函數語句有用逗號分隔開的四部分:存儲在兩個變量中的兩個 DNA 片斷、一個換行符和鏈接符參數。
在結束這一小節以前,讓咱們來看看 Python變量的其餘用法吧。你已經看到,使用變量能夠存儲 DNA 序列數據的字符串。還有其餘類型的數據,編程語言也須要變量來存儲它們。在 Python 中,一個像 DNA 這樣的標量變量能夠存儲字符串、整數、浮點數(有小數點的數字)、布爾值(True 或 False)等。如今,試着在例 4.1或例 4.2中添加以下幾行,在標量變量中存儲一個數字並把它打印出來:
number = 17 print(number, "\n")
做爲生物信息學中的 Python 程序員,你很大一部分時間都是在作相似於例 4.1和例 4.2那樣的事情:你獲取一些數據,多是 DNA、蛋白質、GenBank 條目或者其餘數據,處理這些數據,並把處理結果輸出打印出來。例 4.3是處理 DNA 的另外一個程序:它把 DNA 轉錄成 RNA。在細胞中,把 DNA 轉錄成 RNA 是由精巧、複雜且有勘誤功能的分子機器完成的。此處僅是簡單的替換而已。當 DNA 轉錄成 RNA 時,全部的 T 都會被替換成 U,這也正是咱們的程序所要作的所有工做。
#!/usr/bin/env python # Transcribing DNA into RNA # The DNA DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' # Print the DNA onto the screen print("Here is the starting DNA:\n\n") print("%s\n\n" % DNA) # Transcribe the DNA to RNA by substituting all T's with U's. RNA = DNA.replace('T', 'U') # Print the RNA onto the screen print("Here is the result of transcribing the DNA to RNA:\n\n") print("%s\n" % RNA) # Exit the program. exit()
這是例 4.3的輸出:
Here is the starting DNA: ACGGGAGGACGGGAAAATTACTACGGCATTAGC Here is the result of transcribing the DNA to RNA: ACGGGAGGACGGGAAAAUUACUACGGCAUUAGC
這個簡短的程序展現了 Python 重要的特性:輕鬆處理 DNA 字符串等文本數據的能力。
相似的處理可能會有多種:翻譯、反轉、替換、刪除和重排序等等。總的來講,Python 在此類任務中的便利是其可以在生物信息學領域成功及在程序員中普遍應用的主要緣由。
首先,程序生成了一個 DNA 的拷⻉,並把它存儲在叫作 RNA 的變量中:
RNA = DNA
注意在執行這個語句後,叫作 RNA 的這個變量存儲的就是 DNA。你能夠給變量起任何你喜歡的名字,這是徹底合法的,但不許確的變量名可能會致使一些混亂。在這個例子中,拷⻉完變量值後,緊跟着的是內容豐富的註釋,以後即是讓 RNA 變量真正包含 RNA 的語句,因此此處給它起名爲 RNA 徹底沒有問題。有一個讓 RNA 只包行 RNA 而不包含其餘內容的方法:
RNA = DNA.replace('T', 'U')
在例 4.3中,轉錄過程發生在這個語句中:
RNA = DNA.replace('T', 'U')
在這個語句中有兩個新的項目:賦值操做符(=)和替換函數 replace。很明顯,賦值操做符 = 用於包含字符串的變量,如此處的 RNA 變量儲存 DNA 序列轉錄後的RNA序列數據。replace函數就是「把 DNA 變量存儲的字符串數據中的全部 T 都替換成 U」。
字符串操做對於文本處理來講相當重要,在後續的章節中你將看到,字符串操做是 Python 最爲強大的特性之一。
對於 Python 程序員來講,最重要的資源即是 Python 的文檔。它應該已經安裝在了你的電腦上,另外經過因特網在 Python 網站上也能夠找到它。對於不一樣的計算機系統來講,Python 文檔的格式可能有少量的差異,但網絡版對任何一我的來講都是同樣的,這也正式我在本書中參考的版本。參看附錄 A中的參考資料,你會找到對於 Python 文檔不一樣資源的討論。
來試一下,讓咱們找找 print 操做符的文檔吧。首先,打開你的網⻚瀏覽器,進入 https://www.python.org/ 網站,而後點擊python3.x文檔連接,依次選擇「通常索引」(」General Index」)。你會看到一個把 Python 對象按字⺟順序進行排列的一個冗⻓的列表。一旦你找到這個⻚面,你可能須要把它收藏到瀏覽器的書籤中,由於你會頻繁地訪問該⻚面。
如今點擊 print 來查看 print 函數的文檔吧。
看一下文檔中的例子,看看 Python 語言的特性是如何被運用的。這每每是你找到所需內容的最快方法。
一旦在你的屏幕中打開了文檔,你會發現經過閱讀它會找到一些答案,但也會產生其餘一些疑問。文檔試圖以一種簡潔的形式把全部的內容都包含進去,但這卻會讓初學者們心生膽怯。好比,print 函數文檔的開頭還比較簡單:「將對象打印到文本流文件,以sep分隔,而後結束。 sep,end,file和flush(若是存在)必須做爲關鍵字參數給出」。但以後就是一堆的胡言亂語了(在你學習的現階段它看起來確實是這樣):sep?end?file?flush?
文檔中的全部信息都是必需的,畢竟,你須要在某個地方找到這樣的所有內容!一般狀況下,你能夠忽略掉那些對你來講毫無心義的內容。
Python 文檔中也包含了一些對學習 Python 有很大幫助的教程。有時,它們會假定你掌握了比一個編程語言初學者應當掌握的知識更多的知識,但你會發現它們仍然很是有用。翻閱文檔是在學習 Python 語言過程當中快速成⻓的絕佳途徑。
第 1 章中已經提到了,DNA 聚合物是由核苷酸構成的。考慮到 DNA 雙螺旋中兩條鏈的親密關係,最好能編寫這樣一個程序:給出一條鏈,輸出另外一條鏈。這樣的工做對許多生物信息學應用程序來講都是很是重要的一部分。好比,當在數據庫中查詢某條 DNA 時,經常也須要自動查詢該 DNA 的反向互補序列,由於你手上的序列有多是某個已知基因的負鏈。回到正題,這是例 4.4,它使用了一些新的 Python 特性。就像你將看到的那樣,它首先嚐試一種方法,失敗了,而後嘗試另一種方法,最終取得了成功。
#!/usr/bin/perl -w # Calculating the reverse complement of a strand of DNA # The DNA DNA = 'ACGGGAGGACGGGAAAATTACTACGGCATTAGC' # Print the DNA onto the screen print("Here is the starting DNA:\n\n") print("%s\n\n" % DNA) # Calculate the reverse complement # Warning: this attempt will fail! # # First, copy the DNA into new variable revcom # (short for REVerse COMplement) # Notice that variable names can use lowercase letters like # "revcom" as well as uppercase like "DNA". In fact, # lowercase is more common. # # It doesn't matter if we first reverse the string and then # do the complementation; or if we first do the complementation # and then reverse the string. Same result each time. # So when we make the copy we'll do the reverse in the same statement. # revcom = DNA[::-1] # # Next substitute all bases by their complements, # A->T, T->A, G->C, C->G # revcom = revcom.replace('A', 'T') revcom = revcom.replace('T', 'A') revcom = revcom.replace('G', 'C') revcom = revcom.replace('C', 'G') # Print the reverse complement DNA onto the screen print("Here is the reverse complement DNA:\n\n") print("%s\n" % revcom) # # Oh-oh, that didn't work right! # Our reverse complement should have all the bases in it, since the # original DNA had all the bases--but ours only has A and G! # # Do you see why? # # The problem is that the first two substitute commands above change # all the A's to T's (so there are no A's) and then all the # T's to A's (so all the original A's and T's are all now A's). # Same thing happens to the G's and C's all turning into G's. # print("\nThat was a bad algorithm, and the reverse complement was wrong!\n") print("Try again ... \n\n") # Make a new copy of the DNA (see why we saved the original?) revcom = DNA[::-1] # See the text for a discussion of str.maketrans('ATCGatcg', 'TAGCtagc') revcom =revcom.translate(str.maketrans('ATCGatcg', 'TAGCtagc')) # Print the reverse complement DNA onto the screen print("Here is the reverse complement DNA:\n\n") print("%s\n" % revcom) print("\nThis time it worked!\n\n") exit()
這是你屏幕上例 4.4的輸出:
Here is the starting DNA: ACGGGAGGACGGGAAAATTACTACGGCATTAGC Here is the reverse complement DNA: GGAAAAGGGGAAGAAAAAAAGGGGAGGAGGGGA That was a bad algorithm, and the reverse complement was wrong! Try again ... Here is the reverse complement DNA: GCTAATGCCGTAGTAATTTTCCCGTCCTCCCGT This time it worked!
經過從不一樣的末端開始讀起,也就是說一條鏈從左向右讀,另外一條鏈從右向左讀,你能夠檢查一下 DNA 的兩條鏈是否是反向互補的。當你讀這兩條鏈的時候,比較它們對應的鹼基,應該老是 C 和 G 對應、A 和 T 對應。
在第一次嘗試中,試着從原始的 DNA 和反向互補後的 DNA 中讀幾個字符,你會發現反向互補的第一次嘗試失敗了。這是一個錯誤的算法。
這是在你程序中常常要經歷的一種體驗。你寫一個程序來完成某項任務,但卻發現它並無像你指望的那樣工做。在這種狀況下,咱們須要運用已掌握的知識來嘗試解決全新的問題。它並無如指望那樣完成任務,哪裏出錯了呢?
你會發現這樣的經歷會很是類似:你編寫代碼,但它並不工做!而後你就修正語法(這一般是最簡單的,並且利用錯誤信息提供的線索就能夠輕鬆修正語法),或者對問題進行思考,找到問題所在,並嘗試設計一種新的能夠成功的方法。一般狀況下,這須要你瀏覽語言的文檔,查找語言工做過程的細節內容,同時指望能找到一個解決問題的特性。
若是這個問題在計算機上可以解決,那麼你用 Python 也能夠將它解決。問題是,可以精確到什麼程度?
在例 4.4中,計算反向互補的第一次嘗試失敗了。使用四個全局替換,序列中的每個鹼基都做爲一個總體進行了處理。還須要另外的方法。你能夠從左向右查閱 DNA,每次只查找一種鹼基,把它替換成互補的鹼基,而後再在 DNA 中查找另一種鹼基,一直到字符串的結尾。而後把字符串反轉過來,任務就完成了。事實上,這是一個很是好的方法,在 Python 中也不難實現。但還須要學習語言的其餘內容才行,這將在第 5 章中進行講解。
然而,在這個例子中,maketrans先構建了ATCG對應的轉換關係,而後translate根據轉換關係翻譯序列。
序列切片也是咱們須要的,雖然有點大材小用了。它被設計用來反轉元素的順序,包括字符串,就像在例 4.4中看到的那樣。
到如今爲止,咱們已經編寫了處理 DNA 序列數據的程序,如今,咱們也將要處理一樣重要的蛋白質序列數據。接下來的幾個小節將要學習一些新的知識,這裏是一個簡單的概述:
程序要和計算機磁盤中的文件進行交互。這些文件能夠存儲在任何永久性存儲介質中 ——硬盤、光盤、軟盤、Zip 磁盤、磁帶等。
讓咱們看看如何從文件中讀取蛋白質序列數據吧。首先,在你的計算機上建立一個文件(使用文本編輯器),並在其中存儲一些蛋白質序列數據。給這個文件起名爲
NM_021964fragment.pep(你能夠在書籍網站上下載到該文件)。你將使用下列數據(人類鋅指蛋白 NM_021964 的一部分):
MNIDDKLEGLFLKCGGIDEMQSSRTMVVMGGVSGQSTVSGELQD SVLQDRSMPHQEILAADEVLQESEMRQQDMISHDELMVHEETVKNDEEQMETHERLPQ GLQYALNVPISVKQEITFTDVSEQLMRDKKQIR
你能夠給它起任意一個名字,只要和文件夾中已有的文件不重名便可。
就像好的變量名對於理解程序相當重要同樣,好的文件名和文件夾名也是很是重要的。若是你有一個項目,這個項目會生成大量的文件,那你就須要認真考慮如何對這些文件和文件夾命名和組織了。無論是對於某一個研究人員仍是對於一個龐大的多國團隊來講,這都是很是現實的問題。花一些功夫來給文件起一個富含信息量的名字是很是重要的。文件名 NM_021964fragment.pep 來源於蛋白質的 GenBank ID。同時,它還代表了這只是數據的一部分,而文件的後綴.pep 則提醒你文件中保存的是肽或蛋白質序列數據。固然,其餘的命名方案可能會更加適合於你。無論怎樣,關鍵的一點就是不須要打開文件,僅僅經過文件名就能夠對文件保存的數據有所瞭解。你已經建立或者下載了保存有蛋白質序列數據的文件,那咱們就來編寫一個程序吧,這個程序從文件中讀入蛋白質序列數據並把它保存到變量中。例 4.5是第一次嘗試,隨着學習咱們會逐步對它進行擴展。
#!/usr/bin/env python # Reading protein sequence data from a file # The filename of the file containing the protein sequence data proteinfilename = 'NM_021964fragment.pep' # First we have to "open" the file, and associate # a "filehandle" with it. We choose the filehandle # PROTEINFILE for readability. PROTEINFILE = open(proteinfilename) # Now we do the actual reading of the protein sequence data from the file, # by using the read function to get the input from the # filehandle. We store the data into our variable protein. protein = PROTEINFILE.readline() # Now that we've got our data, we can close the file. PROTEINFILE.close() # Print the protein onto the screen print("Here is the protein:\n\n") print(protein) exit()
下面是例 4.5的輸出:
Here is the protein: MNIDDKLEGLFLKCGGIDEMQSSRTMVVMGGVSGQSTVSGELQD
注意只有文件的第一行被打印輸出出來,稍後我會解釋爲何會這樣。
讓咱們仔細研究一下例 4.5吧。在把文件名保存到 proteinfilename 變量中後,下面這個語句會打開文件:
PROTEINFILE = open(proteinfilename)
打開文件後,你就能夠對它進行各類操做了,好比讀取、寫入、查找、定位到文件的特定位置、清除文件中的全部數據,等等。注意,程序假設保存在 proteinfilename 變量中的文件是存在的,並且能夠打開。你將會看到如何來確認這一點,如今你能夠進行一下這樣的嘗試:更改 proteinfilename 變量中文件的名字,這樣計算機上就沒有叫原來那個名字的文件了,以後運行一下程序。若是文件並不存在,你會看到一些錯誤信息。
若是你查閱 open 函數的文檔,你會看到好多選項,大多數選項都是在打開文件後讓你精確指定如何使用文件的。
讓咱們來看一下 PROTEINFILE 這個數據,它叫作文件句柄。沒有必要理解文件句柄真正是什麼,它們就是你處理文件時使用的東西。對於文件句柄,不必定必須使用大寫字⺟,但這是一個廣爲接受的慣例。在使用 open 語句對文件句柄賦值後,對文件進行的任何交互操做都將經過使用文件句柄來進行。
使用這個語句,就能夠把數據讀入到程序中了:
protein = PROTEINFILE.readline()
經過調用文件句柄的readline函數,你就能夠從程序外部的來源中讀入數據了。在這個例子中,咱們讀入了 NM_021964fragment.pep 文件中的數據,該文件的名字保存在 proteinfilename 變量中,而 open 語句則把它和文件句柄關聯了起來。數據如今就保存到了 protein 變量中,以後能夠把它打印輸出出來。
然而,就像咱們前面已經注意到的那樣,只有這個多行文件的第一行被打印了出來。這是問什麼呢?由於對於讀取文件還有一些知識須要學習。有許多方法能夠讀取整個文件,例 4.6演示了其中的一種。
#!/usr/bin/envl python # Reading protein sequence data from a file, take 2 # The filename of the file containing the protein sequence data proteinfilename = 'NM_021964fragment.pep' # First we have to "open" the file, and associate # a "filehandle" with it. We choose the filehandle # PROTEINFILE for readability. PROTEINFILE = open(proteinfilename) # Now we do the actual reading of the protein sequence data from the file, # by using the function readline to get the input from the # filehandle. We store the data into our variable $protein. # # Since the file has three lines, and since the read only is # returning one line, we'll read a line and print it, three times. # First line protein = PROTEINFILE.readline() # Print the protein onto the screen print("\nHere is the first line of the protein file:\n\n") print($protein) # Second line protein = PROTEINFILE.readline() # Print the protein onto the screen print("\nHere is the second line of the protein file:\n\n") print(protein) # Third line protein = PROTEINFILE.readline() # Print the protein onto the screen print("\nHere is the third line of the protein file:\n\n") print(protein) # Now that we've got our data, we can close the file. PROTEINFILE.close() exit()
下面是例 4.6的輸出:
Here is the first line of the protein file: MNIDDKLEGLFLKCGGIDEMQSSRTMVVMGGVSGQSTVSGELQD Here is the second line of the protein file: SVLQDRSMPHQEILAADEVLQESEMRQQDMISHDELMVHEETVKNDEEQMETHERLPQ Here is the third line of the protein file: GLQYALNVPISVKQEITFTDVSEQLMRDKKQIR
這個程序中比較有趣的一點就是,它演示瞭如何成功得從文件中讀取數據。每當把數據讀取到 protein 這樣的變量中後,都會對文件的下一行進行讀取。上一次讀取到哪兒了,如今須要讀取哪一行,程序對這些信息都有所記錄。
另外一方面,程序的缺陷也是顯而易⻅的。對於輸入文件的每個行,都須要編寫一行代碼來讀入,這樣太不方便了。可是,在 Python 中有兩個特性能夠很好的解決這個問題:列表(下一小節進行介紹)和循環(參看第 5 章)。
在計算機語言中,列表是存儲多個標量值的變量。這些標量的值能夠是數字、字符串,或者,像此處的例子同樣,也能夠是蛋白質序列數據輸入文件中的每一行。讓咱們看看如何來使用列表吧。例 4.7演示瞭如何使用列表來把輸入文件中的全部行都讀入進來。
#!/usr/bin/env python # Reading protein sequence data from a file, take 3 # The filename of the file containing the protein sequence data proteinfilename = 'NM_021964fragment.pep' # First we have to "open" the file PROTEINFILE = open(proteinfilename) # Read the protein sequence data from the file, and store it # into the array variable proteins proteins = PROTEINFILE.readlines() # Print the protein onto the screen print(*proteins, sep='') # Close the file. PROTEINFILE.close() exit()
下面是例 4.7的輸出:
MNIDDKLEGLFLKCGGIDEMQSSRTMVVMGGVSGQSTVSGELQD SVLQDRSMPHQEILAADEVLQESEMRQQDMISHDELMVHEETVKNDEEQMETHERLPQ GLQYALNVPISVKQEITFTDVSEQLMRDKKQIR
就像你看到的,這就是文件中的所有數據。咱們成功了!使用數組的方便性顯而易⻅——只須要一行代碼就能夠把全部的數據都讀入到程序中了。
請注意,Python中的變量能夠賦值爲任何類型,另外,「*」將多個元素的列表解包爲print函數的多個對象,而後print函數打印每一個對象。
列表是存儲多個標量值的變量。列表中的每個項目或者元素都是標量值,能夠經過在列表中的位置(它的下標或者偏移量)對它進行引用。讓咱們來看幾個列表和列表經常使用操做的實例吧。咱們定義一個叫作 bases 的輸出,其中存儲着 A、C、G 和 T 四個鹼基。如今咱們對它應用幾個常⻅的列表操做。
這是一個代碼片斷,它演示瞭如何初始化列表,以及如何使用下標來訪問列表中的單個元素:
# Here's one way to declare an array, initialized with a list of four scalar values. bases = ['A', 'C', 'G', 'T'] # Now we'll print each element of the array print("Here are the array elements:") print("\nFirst element: ") print(bases[0]) print("\nSecond element: ") print(bases[1]) print("\nThird element: ") print(bases[2]) print("\nFourth element: ") print(bases[3])
這個代碼片斷將會輸出:
First element: A Second element: C Third element: G Fourth element: T
你能夠像這樣把元素一個接一個的輸出出來:
bases = ['A', 'C', 'G', 'T'] print("\n\nHere are the array elements: ") print(*bases, sep='')
它會產生這樣的輸出:
Here are the array elements: ACGT
你也能夠輸出用空格分隔的元素(注意 print 語句中的sep參數):
bases = ['A', 'C', 'G', 'T'] print("\n\nHere are the array elements: ") print(*bases)
它會產生這樣的輸出:
Here are the array elements: A C G T
使用 pop,你能夠從列表的末尾拿掉一個元素:
bases = ['A', 'C', 'G', 'T'] base1 = bases.pop() print("Here's the element removed from the end: ") print(base1, "\n\n") print("Here's the remaining array of bases: ") print(*bases)
它會產生這樣的輸出:
Here's an element removed from the beginning: A Here's the remaining array of bases: C G T
使用 pop,你也能夠從列表的開頭拿掉一個鹼基:
bases = ['A', 'C', 'G', 'T'] base2 = bases.pop(0) print("Here's an element removed from the beginning: ") print(base2, "\n\n") print("Here's the remaining array of bases: ") print(*bases)
它會產生這樣的輸出:
Here's an element removed from the beginning: A Here's the remaining array of bases: C G T
使用insert,你能夠把一個元素添加到列表的開頭:
bases = ['A', 'C', 'G', 'T'] base1 = bases.pop() bases.insert(0, base1) print("Here's the element from the end put on the beginning: ") print(*bases, "\n\n")
它會產生這樣的輸出:
Here's the element from the end put on the beginning: T A C G
使用append,你能夠把一個元素添加到列表的末尾:
bases = ['A', 'C', 'G', 'T'] base2 = bases.pop(0) bases.append(base2) print("Here's the element from the beginning put on the end: ") print(*bases, "\n\n")
它會產生這樣的輸出:
Here's the element from the beginning put on the end: C G T A
使用reverse函數,反轉列表:
bases = ['A', 'C', 'G', 'T'] reverse = bases.reverse() print("Here's the array in reverse: ") print(reverse, "\n\n")
它會產生這樣的輸出:
Here's the array in reverse: T G C A
使用len函數,計算列表長度:
bases = ['A', 'C', 'G', 'T'] print("Here's the length of the array: ", len(bases))
它會產生這樣的輸出:
Here's the length of the array: 4
使用insert函數,你能夠在數組的任意一個位置插入一個元素:
bases = ['A', 'C', 'G', 'T'] bases.insert(2, 'X') print("Here's the array with an element inserted after the 2nd element: ", *bases)
它會產生這樣的輸出:
Here's the array with an element inserted after the 2nd element: A C X G T
習題 4.1
探索編程語言對語法錯誤的敏感性。對於一個能夠運行的程序,試着把其中任意一個語句末尾的分號刪除掉,看看會產生什麼樣的錯誤信息。試着改變其餘的語法項:添加一個小括號或者大括號,把「print」或其餘保留字拼錯,鍵入或者刪除任何一些內容。程序員對這樣的錯誤習覺得常。即便精通語言後,在你一點點編寫代碼時,仍然會經常出現這樣的語法錯誤。注意一個錯誤是如何致使多行錯誤報告的。Python 報告的出現錯誤的行是否是徹底準確?
習題 4.2
編寫一個程序,把一個整數保存到變量中並把它打印出來。
習題 4.3
編寫一個把 DNA(原始序列能夠是大寫或者小寫)以小寫形式(acgt)輸出的程序;再編寫一個把 DNA 以大寫形式(ACGT)輸出的程序。
習題 4.4
任務和練習 4.3 中的同樣,但此次使用maketrans和translate函數。
習題 4.5
有時,信息也能夠從 RNA 流向 DNA。編寫一個把 RNA 反轉錄成 DNA 的程序。
習題 4.6
讀取兩個文件的數據,在輸出第一個文件的內容後緊接着輸出第一個文件的內容。
習題 4.7
這是一個更有難度的練習題。編寫一個程序,讀去一個文件,而後逆序輸出它的每一行,也就是首先輸出它的最後一行。你可能須要看一下pop、insert 和 reverse 這三個函數,從中選擇一個或多個來完成此練習。你可能須要預習一下第 5 章,這樣就能夠在程序中使用循環了。但取決於你採起的方案,這並非必需的。或者,你能夠對包含全部行的列表使用 reverse 函數。