摘要:函數(shù)體由表達式組成。我們說頭部控制語句組。于是,函數(shù)體內的賦值語句不會影響全局幀。包含了多種假值,包括和布爾值。布爾值表示了邏輯表達式中的真值。執(zhí)行測試以及返回布爾值的函數(shù)通常以開頭,并不帶下劃線例如等等。返回值之后會和預期結果進行比對。
1.5 控制
來源:1.5 Control
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
我們現(xiàn)在可以定義的函數(shù)能力有限,因為我們還不知道一種方法來進行測試,并且根據(jù)測試結果來執(zhí)行不同的操作。控制語句可以讓我們完成這件事。它們不像嚴格的求值子表達式那樣從左向右編寫,并且可以從它們控制解釋器下一步做什么當中得到它們的名稱。這可能基于表達式的值。
1.5.1 語句目前為止,我們已經(jīng)初步思考了如何求出表達式。然而,我們已經(jīng)看到了三種語句:賦值、def和return語句。這些 Python 代碼并不是表達式,雖然它們中的一部分是表達式。
要強調的是,語句的值是不相干的(或不存在的),我們使用執(zhí)行而不是求值來描述語句。
每個語句都描述了對解釋器狀態(tài)的一些改變,執(zhí)行語句會應用這些改變。像我們之前看到的return和賦值語句那樣,語句的執(zhí)行涉及到求解所包含的子表達式。
表達式也可以作為語句執(zhí)行,其中它們會被求值,但是它們的值會舍棄。執(zhí)行純函數(shù)沒有什么副作用,但是執(zhí)行非純函數(shù)會產生效果作為函數(shù)調用的結果。
考慮下面這個例子:
>>> def square(x): mul(x, x) # Watch out! This call doesn"t return a value.
這是有效的 Python 代碼,但是并不是想表達的意思。函數(shù)體由表達式組成。表達式本身是個有效的語句,但是語句的效果是,mul函數(shù)被調用了,然后結果被舍棄了。如果你希望對表達式的結果做一些事情,你需要這樣做:使用賦值語句來儲存它,或者使用return語句將它返回:
>>> def square(x): return mul(x, x)
有時編寫一個函數(shù)體是表達式的函數(shù)是有意義的,例如調用類似print的非純函數(shù):
>>> def print_square(x): print(square(x))
在最高層級上,Python 解釋器的工作就是執(zhí)行由語句組成的程序。但是,許多有意思的計算工作來源于求解表達式。語句管理程序中不同表達式之間的關系,以及它們的結果會怎么樣。
1.5.2 復合語句通常,Python 的代碼是語句的序列。一條簡單的語句是一行不以分號結束的代碼。復合語句之所以這么命名,因為它是其它(簡單或復合)語句的復合。復合語句一般占據(jù)多行,并且以一行以冒號結尾的頭部開始,它標識了語句的類型。同時,一個頭部和一組縮進的代碼叫做子句(或從句)。復合語句由一個或多個子句組成。
: ... : ... ...
我們可以這樣理解我們已經(jīng)見到的語句:
表達式、返回語句和賦值語句都是簡單語句。
def語句是復合語句。def頭部之后的組定義了函數(shù)體。
為每種頭部特化的求值規(guī)則指導了組內的語句什么時候以及是否會被執(zhí)行。我們說頭部控制語句組。例如,在def語句的例子中,我們看到返回表達式并不會立即求值,而是儲存起來用于以后的使用,當所定義的函數(shù)最終調用時就會求值。
我們現(xiàn)在也能理解多行的程序了。
執(zhí)行語句序列需要執(zhí)行第一條語句。如果這個語句不是重定向控制,之后執(zhí)行語句序列的剩余部分,如果存在的話。
這個定義揭示出遞歸定義“序列”的基本結構:一個序列可以劃分為它的第一個元素和其余元素。語句序列的“剩余”部分也是一個語句序列。所以我們可以遞歸應用這個執(zhí)行規(guī)則。這個序列作為遞歸數(shù)據(jù)結構的看法會在隨后的章節(jié)中再次出現(xiàn)。
這一規(guī)則的重要結果就是語句順序執(zhí)行,但是隨后的語句可能永遠不會執(zhí)行到,因為有重定向控制。
實踐指南:在縮進代碼組時,所有行必須以相同數(shù)量以及相同方式縮進(空格而不是Tab)。任何縮進的變動都會導致錯誤。
1.5.3 定義函數(shù) II:局部賦值一開始我們說,用戶定義函數(shù)的函數(shù)體只由帶有一個返回表達式的一個返回語句組成。實際上,函數(shù)可以定義為操作的序列,不僅僅是一條表達式。Python 復合語句的結構自然讓我們將函數(shù)體的概念擴展為多個語句。
無論用戶定義的函數(shù)何時被調用,定義中的子句序列在局部環(huán)境內執(zhí)行。return語句會重定向控制:無論什么時候執(zhí)行return語句,函數(shù)調用的流程都會中止,返回表達式的值會作為被調用函數(shù)的返回值。
于是,賦值語句現(xiàn)在可以出現(xiàn)在函數(shù)體中。例如,這個函數(shù)以第一個數(shù)的百分數(shù)形式,返回兩個數(shù)量的絕對值,并使用了兩步運算:
>>> def percent_difference(x, y): difference = abs(x-y) return 100 * difference / x >>> percent_difference(40, 50) 25.0
賦值語句的效果是在當前環(huán)境的第一個幀上,將名字綁定到值上。于是,函數(shù)體內的賦值語句不會影響全局幀。函數(shù)只能操作局部作用域的現(xiàn)象是創(chuàng)建模塊化程序的關鍵,其中純函數(shù)只通過它們接受和返回的值與外界交互。
當然,percent_difference函數(shù)也可以寫成一個表達式,就像下面這樣,但是返回表達式會更加復雜:
>>> def percent_difference(x, y): return 100 * abs(x-y) / x
目前為止,局部賦值并不會增加函數(shù)定義的表現(xiàn)力。當它和控制語句組合時,才會這樣。此外,局部賦值也可以將名稱賦為間接量,在理清復雜表達式的含義時起到關鍵作用。
新的環(huán)境特性:局部賦值。
1.5.4 條件語句Python 擁有內建的絕對值函數(shù):
>>> abs(-2) 2
我們希望自己能夠實現(xiàn)這個函數(shù),但是我們當前不能直接定義函數(shù)來執(zhí)行測試并做出選擇。我們希望表達出,如果x是正的,abs(x)返回x,如果x是 0,abx(x)返回 0,否則abs(x)返回-x。Python 中,我們可以使用條件語句來表達這種選擇。
>>> def absolute_value(x): """Compute abs(x).""" if x > 0: return x elif x == 0: return 0 else: return -x >>> absolute_value(-2) == abs(-2) True
absolute_value的實現(xiàn)展示了一些重要的事情:
條件語句。Python 中的條件語句包含一系列的頭部和語句組:一個必要的if子句,可選的elif子句序列,和最后可選的else子句:
if: elif : else:
當執(zhí)行條件語句時,每個子句都按順序處理:
求出頭部中的表達式。
如果它為真,執(zhí)行語句組。之后,跳過條件語句中隨后的所有子句。
如果能到達else子句(僅當所有if和elif表達式值為假時),它的語句組才會被執(zhí)行。
布爾上下文。上面過程的執(zhí)行提到了“假值”和“真值”。條件塊頭部語句中的表達式也叫作布爾上下文:它們值的真假對控制流很重要,但在另一方面,它們的值永遠不會被賦值或返回。Python 包含了多種假值,包括 0、None和布爾值False。所有其他數(shù)值都是真值。在第二章中,我們就會看到每個 Python 中的原始數(shù)據(jù)類型都是真值或假值。
布爾值。Python 有兩種布爾值,叫做True和False。布爾值表示了邏輯表達式中的真值。內建的比較運算符,>、<、>=、<=、==、!=,返回這些值。
>>> 4 < 2 False >>> 5 >= 5 True
第二個例子讀作“5 大于等于 5”,對應operator模塊中的函數(shù)ge。
>>> 0 == -0 True
最后的例子讀作“0 等于 -0”,對應operator模塊的eq函數(shù)。要注意 Python 區(qū)分賦值(=)和相等測試(==)。許多語言中都有這個慣例。
布爾運算符。Python 也內建了三個基本的邏輯運算符:
>>> True and False False >>> True or False True >>> not False True
邏輯表達式擁有對應的求值過程。這些過程揭示了邏輯表達式的真值有時可以不執(zhí)行全部子表達式而確定,這個特性叫做短路。
為了求出表達式
求出子表達式
如果結果v是假值,那么表達式求值為v。
否則表達式的值為子表達式
為了求出表達式
求出子表達式
如果結果v是真值,那么表達式求值為v。
否則表達式的值為子表達式
為了求出表達式not
求出
這些值、規(guī)則和運算符向我們提供了一種組合測試結果的方式。執(zhí)行測試以及返回布爾值的函數(shù)通常以is開頭,并不帶下劃線(例如isfinite、isdigit、isinstance等等)。
1.5.5 迭代除了選擇要執(zhí)行的語句,控制語句還用于表達重復操作。如果我們編寫的每一行代碼都只執(zhí)行一次,程序會變得非常沒有生產力。只有通過語句的重復執(zhí)行,我們才可以釋放計算機的潛力,使我們更加強大。我們已經(jīng)看到了重復的一種形式:一個函數(shù)可以多次調用,雖然它只定義一次。迭代控制結構是另一種將相同語句執(zhí)行多次的機制。
考慮斐波那契數(shù)列,其中每個數(shù)值都是前兩個的和:
0, 1, 1, 2, 3, 5, 8, 13, 21, ...
每個值都通過重復使用“前兩個值的和”的規(guī)則構造。為了構造第 n 個值,我們需要跟蹤我們創(chuàng)建了多少個值(k),以及第 k 個值(curr)和它的上一個值(pred),像這樣:
>>> def fib(n): """Compute the nth Fibonacci number, for n >= 2.""" pred, curr = 0, 1 # Fibonacci numbers k = 2 # Position of curr in the sequence while k < n: pred, curr = curr, pred + curr # Re-bind pred and curr k = k + 1 # Re-bind k return curr >>> fib(8) 13
要記住逗號在賦值語句中分隔了多個名稱和值。這一行:
pred, curr = curr, pred + curr
具有將curr的值重新綁定到名稱pred上,以及將pred + curr的值重新綁定到curr上的效果。所有=右邊的表達式會在綁定發(fā)生之前求出來。
while子句包含一個頭部表達式,之后是語句組:
while:
為了執(zhí)行while子句:
求出頭部表達式。
如果它為真,執(zhí)行語句組,之后返回到步驟 1。
在步驟 2 中,整個while子句的語句組在頭部表達式再次求值之前被執(zhí)行。
為了防止while子句的語句組無限執(zhí)行,它應該總是在每次通過時修改環(huán)境的狀態(tài)。
不終止的while語句叫做無限循環(huán)。按下
函數(shù)的測試是驗證函數(shù)的行為是否符合預期的操作。我們的函數(shù)現(xiàn)在已經(jīng)足夠復雜了,我們需要開始測試我們的實現(xiàn)。
測試是系統(tǒng)化執(zhí)行這個驗證的機制。測試通常寫為另一個函數(shù),這個函數(shù)包含一個或多個被測函數(shù)的樣例調用。返回值之后會和預期結果進行比對。不像大多數(shù)通用的函數(shù),測試涉及到挑選特殊的參數(shù)值,并使用它來驗證調用。測試也可作為文檔:它們展示了如何調用函數(shù),以及什么參數(shù)值是合理的。
要注意我們也將“測試”這個詞用于if或while語句的頭部中作為一種技術術語。當我們將“測試”這個詞用作表達式,或者用作一種驗證機制時,它應該在語境中十分明顯。
斷言。程序員使用assert語句來驗證預期,例如測試函數(shù)的輸出。assert語句在布爾上下文中只有一個表達式,后面是帶引號的一行文本(單引號或雙引號都可以,但是要一致)如果表達式求值為假,它就會顯示。
>>> assert fib(8) == 13, "The 8th Fibonacci number should be 13"
當被斷言的表達式求值為真時,斷言語句的執(zhí)行沒有任何效果。當它是假時,asset會造成執(zhí)行中斷。
為fib編寫的test函數(shù)測試了幾個參數(shù),包含n的極限值:
>>> def fib_test(): assert fib(2) == 1, "The 2nd Fibonacci number should be 1" assert fib(3) == 1, "The 3nd Fibonacci number should be 1" assert fib(50) == 7778742049, "Error at the 50th Fibonacci number"
在文件中而不是直接在解釋器中編寫 Python 時,測試可以寫在同一個文件,或者后綴為_test.py的相鄰文件中。
Doctest。Python 提供了一個便利的方法,將簡單的測試直接寫到函數(shù)的文檔字符串內。文檔字符串的第一行應該包含單行的函數(shù)描述,后面是一個空行。參數(shù)和行為的詳細描述可以跟隨在后面。此外,文檔字符串可以包含調用該函數(shù)的簡單交互式會話:
>>> def sum_naturals(n): """Return the sum of the first n natural numbers >>> sum_naturals(10) 55 >>> sum_naturals(100) 5050 """ total, k = 0, 1 while k <= n: total, k = total + k, k + 1 return total
之后,可以使用 doctest 模塊來驗證交互。下面的globals函數(shù)返回全局變量的表示,解釋器需要它來求解表達式。
>>> from doctest import run_docstring_examples >>> run_docstring_examples(sum_naturals, globals())
在文件中編寫 Python 時,可以通過以下面的命令行選項啟動 Python 來運行一個文檔中的所有 doctest。
python3 -m doctest
高效測試的關鍵是在實現(xiàn)新的函數(shù)之后(甚至是之前)立即編寫(以及執(zhí)行)測試。只調用一個函數(shù)的測試叫做單元測試。詳盡的單元測試是良好程序設計的標志。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/45498.html
摘要:對象表示信息,但是同時和它們所表示的抽象概念行為一致。通過綁定行為和信息,對象提供了可靠獨立的日期抽象。名稱來源于實數(shù)在中表示的方式浮點表示。另一方面,對象可以表示很大范圍內的分數(shù),但是不能表示所有有理數(shù)。 2.1 引言 來源:2.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在第一章中,我們專注于計算過程,以及程序設計中函數(shù)的作用。我們看到了...
摘要:為通用語言設計解釋器的想法可能令人畏懼。但是,典型的解釋器擁有簡潔的通用結構兩個可變的遞歸函數(shù),第一個求解環(huán)境中的表達式,第二個在參數(shù)上調用函數(shù)。這一章接下來的兩節(jié)專注于遞歸函數(shù)和數(shù)據(jù)結構,它們是理解解釋器設計的基礎。 3.1 引言 來源:3.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 第一章和第二章描述了編程的兩個基本元素:數(shù)據(jù)和函數(shù)之間的...
摘要:使用消息傳遞,我們就能使抽象數(shù)據(jù)類型直接擁有行為。構造器以類似的方式實現(xiàn)它在參數(shù)上調用了叫做的方法。抽象數(shù)據(jù)類型允許我們在數(shù)據(jù)表示和用于操作數(shù)據(jù)的函數(shù)之間構造界限。 2.7 泛用方法 來源:2.7 Generic Operations 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 這一章中我們引入了復合數(shù)據(jù)類型,以及由構造器和選擇器實現(xiàn)的數(shù)據(jù)抽象機制。使用消息傳遞,我們就能...
摘要:的最常見的作用是構造異常實例并拋出它。子句組只在執(zhí)行過程中的異常產生時執(zhí)行。每個子句指定了需要處理的異常的特定類。將強制轉為字符串會得到由返回的人類可讀的字符串。 3.4 異常 來源:3.4 Exceptions 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 程序員必須總是留意程序中可能出現(xiàn)的錯誤。例子數(shù)不勝數(shù):一個函數(shù)可能不會收到它預期的信息,必需的資源可能會丟失,或者網(wǎng)...
摘要:另一個賦值語句將名稱關聯(lián)到出現(xiàn)在莎士比亞劇本中的所有去重詞匯的集合,總計個。表達式是一個復合表達式,計算出正序或倒序出現(xiàn)的莎士比亞詞匯集合。在意圖上并沒有按照莎士比亞或者回文來設計,但是它極大的靈活性讓我們用極少的代碼處理大量文本。 1.1 引言 來源:1.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 計算機科學是一個極其寬泛的學科。全球的分布...
閱讀 1606·2021-10-14 09:42
閱讀 3861·2021-09-07 09:59
閱讀 1328·2019-08-30 15:55
閱讀 606·2019-08-30 11:17
閱讀 3368·2019-08-29 16:06
閱讀 536·2019-08-29 14:06
閱讀 3165·2019-08-28 18:14
閱讀 3684·2019-08-26 13:55