<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 第十六章 類和函數 現在我們已經知道如何創建新類型了,下一步就要寫一些函數了,這些函數用自定義類型做參數和返回值。在本章中還提供了一種函數式編程的模式,以及兩種新的程序開發規劃方式。 本章的樣例代碼可以在[這里](http://thinkpython2.com/code/Time1.py)下載。然后練習題的樣例代碼可以在[這里](http://thinkpython2.com/code/Time1_soln.py)下載到。 ## 16.1 時間 下面又是一個自定義類型的例子,這次咱們定義一個叫做 Time 的類,記錄下當日的時間。 類的定義是如下這樣: ```py class Time: """Represents the time of day. attributes: hour, minute, second """ ``` 我們可以建立一個新的 Time 對象,然后對時分秒分別進行賦值: ```py time = Time() time.hour = 11 time.minute = 59 time.second = 30 ``` 這個 Time 對象的狀態圖如圖 16.1 所示。 下面做個練習,寫一個名為 print_time 的函數,接收一個 Time 對象,然后以時:分:秒的格式來輸出。提示:格式序列'%.2d'就會用兩位來輸出一個整數,第一位可以為 0。 寫一個布爾函數,名為 is_after,接收兩個 Time 對象,分別為 t1 和 t2,然后如果 t1 在時間上位于 t2 的后面,就返回真,否則返回假。難度提高一下:不要用 if 語句,看你能搞定不。 ________________________________________ ![Figure 16.1: Object diagram.](http://7xnq2o.com1.z0.glb.clouddn.com/ThinkPython16.1.png) Figure 16.1: Object diagram. ________________________________________ ## 16.2 純函數 后面的這些章節中,我們要寫兩個函數來對 time 進行加法操作。這兩個函數展示了兩種函數類型:純函數和修改器。寫這兩個函數的過程中,也體現了我即將講到的一種新的開發模式:原型和補丁模式,這種方法就是在處理復雜問題的時候,先從簡單的原型開始,然后逐漸解決復雜的內容。 下面這段代碼就是 add_time 函數的一個原型: ```py def add_time(t1, t2): sum = Time() sum.hour = t1.hour + t2.hour sum.minute = t1.minute + t2.minute sum.second = t1.second + t2.second return sum ``` 這個函數新建了一個新的 Time 對象,初始化了所有的值,然后返回了一個對新對象的引用。這種函數叫純函數,因為這種函數并不修改傳來做參數的對象,也沒有什么效果,比如顯示值啊或者讓用戶輸入啊等等,而只是返回一個值而已。 下面就來測試一下這個函數,我將建立兩個 Time 對象,start 包含了一個電影的開始時間,比如《巨蟒與圣杯》(譯者注:1975 年喜劇電影。Python 的創造者 Guido van Rossum 特別喜歡這個喜劇團體:巨蟒飛行馬戲團(Monty Python’s Flying Circus ),所以命名為 Python。),然后 duration(漢譯就是持續時間)包含了該電影的時長,《巨蟒與圣杯》這部電影是一小時三十五分鐘。add_time 函數就會算出電影結束的時間。 ```py >>> start = Time() >>> start.hour = 9 >>> start.minute = 45 >>> start.second = 0 >>> duration = Time() >>> duration.hour = 1 >>> duration.minute = 35 >>> duration.second = 0 >>> done = add_time(start, duration) >>> print_time(done) 10:80:00 ``` 很明顯,10 點 80 分 00 秒這樣的時間肯定不是你想要的結果。問題就出在了函數不值得如何應對時分秒的六十位進位,所以超過 60 的時候沒進位就這樣了。所以我們得把超出六十秒的進位到分,超過六十分的進位到小時。 下面這個是改進版本: ```py def add_time(t1, t2): sum = Time() sum.hour = t1.hour + t2.hour sum.minute = t1.minute + t2.minute sum.second = t1.second + t2.second if sum.second >= 60: sum.second -= 60 sum.minute += 1 if sum.minute >= 60: sum.minute -= 60 sum.hour += 1 return sum ``` 這回函數正確工作了,但代碼也開始變多了。稍后我們就能看到一個短一些的替代方案。 ## 16.3 修改器 有時候需要對作為參數的對象進行一些修改。這時候這些修改就可以被調用者察覺。這樣工作的函數就叫修改器了。 increment 函數,增加給定的秒數到一個 Time 對象,就可以被改寫成一個修改器。 下面是個簡單的版本: ```py def increment(time, seconds): time.second += seconds if time.second >= 60: time.second -= 60 time.minute += 1 if time.minute >= 60: time.minute -= 60 time.hour += 1 ``` 第一行代碼進行了最簡單的運算;后面的代碼是用來應對我們之前討論過的特例情況。 那么這個函數正確么?秒數超過六十會怎么樣? 很明顯,秒數超過六十的時候,就需要運行不只一次了;必須一直運行,之道 time.second 的值小于六十了才行。有一種辦法就是把 if 語句換成 while 語句。這樣就可以解決這個問題了,但效率不太高。 做個練習,寫一個正確的 increment 函數,并且要不包含任何循環。 能用修改器實現的功能也都能用純函數來實現。實際上有的編程語言只允許純函數。有證據表明,與修改器相比,使用修改器能夠更快地開發,而且不容易出錯誤。但修改器往往很方便好用,而函數式的程序一般效率要差一些。 總的來說,我還是建議你寫純函數,除非用修改器有特別顯著的好處。這種模式也叫做函數式編程。 做個練習,寫一個用純函數實現的 increment,創建并返回一個新的 Time 對象,而不是修改參數。 ## 16.4 原型與規劃 這次我演示的開發規劃就是『原型與補丁模式』。對每個函數,我都先謝了一個簡單的原型,只進行基本的運算,然后測試一下,接下來逐步修補錯誤。 這種模式很有效率,尤其是在你對問題的理解不是很深入的時候。不過漸進式的修改也會產生過分復雜的代碼——因為要應對很多特例情況,而且也不太靠靠——因為不好確定你是否找到了所有的錯誤。 另一種模式就是設計規劃開發,這種情況下對問題的深入透徹的理解就讓開發容易很多了。本節中的根本性認識就在于 TIme 對象實際上是一個三位的六十進制數字(參考 [這里的解釋](http://en.wikipedia.org/wiki/Sexagesimal)。)!秒數也就是個位,分數也就是六十位,小時數就是三千六百位。 這樣當我們寫 add_time 和 increment 函數的時候,用 60 進制來進行計算就很有效率。 這一觀察表明有另外一種方法來解決整個問題——我們可以把 Time 對象轉換成整數,然后因為計算機最擅長整數運算,這樣就有優勢了。 下面這個函數就把 Times 轉換成了整數: ```py def time_to_int(time): minutes = time.hour * 60 + time.minute seconds = minutes * 60 + time.second return seconds ``` 然后下面這個函數是反過來的,把整數轉換成 Time(還記得 divmod 么,使用第一個數除以第二個數,返回的是除數和余數組成的元組。) ```py def int_to_time(seconds): time = Time() minutes, time.second = divmod(seconds, 60) time.hour, time.minute = divmod(minutes, 60) return time ``` 你最好先考慮好了,然后多進行幾次測試運行,然后要確保這些函數都是正確的。比如你就可以試著用很多個 x 的值來運算 time_to_int(int_to_time(x)) == x。這也是連貫性檢測的一個例子。 一旦你確定這些函數都沒問題,就可以用它們來重寫一下 add_time 這個函數了: ```py def add_time(t1, t2): seconds = time_to_int(t1) + time_to_int(t2) return int_to_time(seconds) ``` 這個版本就比最開始那個版本短多了,也更容易去檢驗了。接下來就做個聯系吧,用 time_to_int 和 int_to_time 這兩個函數來重寫一下 increment。 在一定程度上,從六十進制到十進制的來回轉換,遠遠比計算時間要麻煩的多。進制轉換要更加抽象很多;我們處理時間計算的直覺要更好些。 然而,如果我們有足夠的遠見,把時間值當做六十進制的數值來對待,然后寫出一些轉換函數(比如 time_to_int 和 int_to_time),就能讓程序變得更短,可讀性更好,調試更容易,也更加可靠。 而且后續添加功能也更容易了。比如,假設要對兩個時間對象進行相減來求二者之間的持續時間。簡單版本的方法就是要用借位的減法。而使用轉換函數的版本就更容易了,也更不容易出錯。 有意思的事,有時候以困難模式來寫一個程序(比如用更加泛化的模式),反而能讓開發更簡單(因為這樣就減少了特例情況,也減少了出錯誤的概率了。) ## 16.5 調試 對于一個 Time 對象來說,只要分和秒的值在 0-60 的前閉后開區間(即可以為 0 但不可以為 60),并且小時數為正數,就是格式正確的。小時和分鐘都應該是整數,但秒是可以為小數的。 像這些要求也叫約束條件,因為通常都得滿足這些條件才行。反過來說,如果這些條件沒滿足,就有可能是程序中某處存在錯誤了。 寫一些檢測約束條件的代碼,能夠幫助找出這些錯誤,并且找到導致錯誤的原因。例如,你虧寫一個名字未 calid_time 的函數,接收一個 Time 對象,然后如果該對象不滿足約束條件就返回 False: ```py def valid_time(time): if time.hour < 0 or time.minute < 0 or time.second < 0: return False if time.minute >= 60 or time.second >= 60: return False return True ``` 然后在每個自定義函數的開頭部位,你就可以檢測一下參數,來確保這些參數沒有錯誤: ```py def add_time(t1, t2): if not valid_time(t1) or not valid_time(t2): raise ValueError('invalid Time object in add_time') seconds = time_to_int(t1) + time_to_int(t2) return int_to_time(seconds) ``` 或者你也可以用一個 assert 語句,這個語句也是檢測給定的約束條件的,如果出現錯誤就會拋出一個異常: ```py def add_time(t1, t2): assert valid_time(t1) and valid_time(t2) seconds = time_to_int(t1) + time_to_int(t2) return int_to_time(seconds) ``` assert 語句是很有用的,可以用來區分條件語句的用途,將 assert 這種用于檢查錯誤的語句與常規的條件語句在代碼上進行區分。 ## 16.6 Glossary 術語列表 prototype and patch: A development plan that involves writing a rough draft of a program, testing, and correcting errors as they are found. >原型和補丁模式:一種開發模式,先寫一個程序的草稿,然后測試,再改正發現的錯誤,這樣逐步演化的開發模式。 designed development: A development plan that involves high-level insight into the problem and more planning than incremental development or prototype development. >設計規劃開發:這種開發模式要求對所面對問題的高程度的深刻理解,相比漸進式開發和原型增補模式要更具有計劃性。 pure function: A function that does not modify any of the objects it receives as arguments. Most pure functions are fruitful. >純函數:不修改參數對象的函數。這種函數多數是有返回值的函數。 modifier: A function that changes one or more of the objects it receives as arguments. Most modifiers are void; that is, they return None. >修改器:修改參數對象的函數。大多數這樣的函數都是無返回值的,也就是返回的都是 None。 functional programming style: A style of program design in which the majority of functions are pure. >函數式編程模式:一種程序設計模式,主要特征為大多數函數都是純函數。 invariant: A condition that should always be true during the execution of a program. >約束條件:在程序運行過程中,應該一直為真的條件。 assert statement: A statement that check a condition and raises an exception if it fails. >assert 語句:一種檢查錯誤的語句,檢查一個條件,如果不滿足就拋出異常。 ## 16.7 練習 本章的例子可以在 [這里](http://thinkpython2.com/code/Time1.py)下載;練習題的答案可以在[這里](http://thinkpython2.com/code/Time1_soln.py)下載。 ### 練習 1 寫一個函數,名為 mul_time,接收一個 Time 對象和一個數值,返回一個二者相乘得到的新的 Time 對象。 然后用 mul_time 這個函數寫一個函數,接收一個 Time 對象,代表著一個比賽的結束時間,還有一個數值,代表比賽距離,然后返回一個表示了平均步調(單位距離花費的時間)的新的 Time 對象。 ### 練習 2 datetime 模塊提供了一些 time 對象,和本章的 Time 對象很相似,但前者提供了更多的方法和運算符。讀一讀[這里的文檔] [Here](http://docs.python.org/3/library/datetime.html)吧。 1. 用 datetime 模塊來寫一個函數,獲取當前日期,然后輸出今天是星期幾。 2.寫一個函數,要求輸入生日,然后輸出用戶的年齡以及距離下一個生日的日、時、分、秒數。 3.有的兩個人在不同日期出生,會在某一天,一個人的年齡是另外一個人年齡的兩杯。這一天就叫做他們的雙倍日。寫一個函數,接收兩個生日,然后計算雙倍日。 4. 再來點有挑戰性的,寫一個更通用的版本,來計算一下一個人的年齡為另外一個人年齡 n 倍時候的日期。 [樣例代碼](http://thinkpython2.com/code/double.py)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看