1. 程式人生 > >《Python編程:從入門到實踐》第10章 筆記

《Python編程:從入門到實踐》第10章 筆記

import 我沒 r+ 數據結構 中新 1.5 err 因此 重構

備註:以下按照Python 2的規則編寫代碼。
在jupyter notebook中運行直接顯示結果。

10 文件和異常

10.1 文件中讀取數據

10.1.1 讀取整個文件


"""file_reader.py"""

with open(‘chapter_10/pi_digits.txt‘) as file_object:   # 關鍵字with在不再需要訪問文件後將其關閉
    contents = file_object.read()
    print(contents)
    
3.1415926535
  8979323846
  2643383279

10.1.2


"""file_reader.py"""
with open(‘chapter_10/pi_digits.txt‘) as file_object: # 關鍵字with在不再需要訪問文件後將其關閉 contents = file_object.read() print(contents.rstrip()) # 使用rstrip()刪除文件末尾多余的空行
3.1415926535
  8979323846
  2643383279

10.1.3 逐行讀取


"""file_reader.py"""

filename = ‘chapter_10/pi_digits.txt‘

with open(filename) as
f: for line in f: print line with open(filename) as f: for line in f: print line.rstrip()
3.1415926535

  8979323846

  2643383279

3.1415926535
  8979323846
  2643383279

10.1.4 創建包含文件各行內容的列表



filename = ‘chapter_10/pi_digits.txt‘

with open(filename) as f:
    lines = f.readlines()
    
print
lines for line in lines: print line.rstrip()
[‘3.1415926535\n‘, ‘  8979323846\n‘, ‘  2643383279\n‘]
3.1415926535
  8979323846
  2643383279

10.1.5 使用文件中的內容


"""pi_string.py"""

filename = ‘chapter_10/pi_digits.txt‘

with open(filename) as f:
    lines = f.readlines()
    
    
pi_string = ‘‘
for line in lines:
    pi_string += line.rstrip()
    
print pi_string
print len(pi_string)
3.1415926535  8979323846  2643383279
36

"""pi_string.py"""

filename = ‘chapter_10/pi_digits.txt‘

with open(filename) as f:
    lines = f.readlines()
    
    
pi_string = ‘‘
for line in lines:
    pi_string += line.strip()  # 刪除前後的空格
    
print pi_string
print len(pi_string)
3.141592653589793238462643383279
32

註意:
讀取文本文件時,Python將其中的所有文本都解讀為字符串。如果讀取的是數字,並要將其作為數值使用,就必須使用函數int()將其轉換為整數,或使用函數float()將其轉換為浮點數。

10.1.6 包含一百萬位的大型文件


"""pi_string.py"""

filename = ‘chapter_10/pi_million_digits.txt‘

with open(filename) as f:
    lines = f.readlines()
    
    
pi_string = ‘‘
for line in lines:
    pi_string += line.strip()
    
print pi_string[:52] + "..."    # 以打印小數點後50位為例
print len(pi_string)
3.14159265358979323846264338327950288419716939937510...
1000002

10.1.7 圓周率值中包含你的生日嗎



"""任務:確定某個生日是否包含在圓周率值的前1 000 000位中。"""


filename = ‘chapter_10/pi_million_digits.txt‘

with open(filename) as f:
    lines = f.readlines()
    
    
pi_string = ‘‘
for line in lines:
    pi_string += line.rstrip()
    
birthday = raw_input("Enter your birthday, in the form mmddyy: ")
if birthday  in pi_string:
    print "Your birthday aperars in the first million digits of pi!"
    
else:
    print "Your birthday doesn‘t appear in the first million digits of pi."
    
Enter your birthday, in the form mmddyy: 120372
Your birthday aperars in the first million digits of pi!

習題: 10-1 Python學習筆記


"""
在文本編輯器中新建一個文件,寫幾句話來總結一下你至此學到的 Python 知識,其中每一行都以“In Python you can”打頭。
將這文件命名為learning_python.txt,並將其存儲到為完成本章練習而編寫的程序所在的目錄中。編寫一個程序,它讀取這個
文件,並將你所寫的內容打印三次:第一次打印時讀取整個文件;第二次打印時遍歷文件對象;第三次打印時將各行存儲在一個列
表中,再在 with 代碼塊外打印它們。
"""

with open(‘chapter_10/learning_python.txt‘) as lp:
    contents = lp.read()

print "1>>>" 
print contents


print "\n2>>>"
for line in contents:
    print line.strip()

print "\n3>>>"
with open(‘chapter_10/learning_python.txt‘) as lp:
    lines = lp.readlines()
    for line in lines:
        print line.rstrip()

    
1>>>
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.

2>>>
I
n

P
y
t
h
o
n

y
o
u

c
a
n

d
o

w
h
a
t
e
v
e
r

y
o
u

w
a
n
t
!

I
n

P
y
t
h
o
n

y
o
u

c
a
n

r
e
a
d

d
i
f
f
e
r
e
n
t

t
p
y
e
s

o
f

f
i
l
e
s
.

I
n

P
y
t
h
o
n

y
o
u

c
a
n

d
r
a
w

s
o
m
e
t
h
i
n
g
.

I
n

P
y
t
h
o
n

y
o
u

c
a
n

p
r
o
g
r
a
m

i
n
t
e
r
e
s
t
i
n
g

g
a
m
e
s
.

3>>>
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.

練習:10-2 C語言學習筆記


"""
讀取你剛創建的文件 learning_python.txt 中的每一行,使用方法replace()將其中的 Python 都替換為另一門語言的名稱,
如 C。將修改後的各行都打印到屏幕上。 
"""

message = "I really like dogs."
message.replace(‘dog‘, ‘cat‘)

with open(‘chapter_10/learning_python.txt‘) as f:
    contents = f.read()
    
print contents
print "\nChange ‘Python‘ to ‘C‘."
print contents.replace("Python", "C")   # replace()替換字符串中特定單詞
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.

Change ‘Python‘ to ‘C‘.
In C you can do whatever you want!
In C you can read different tpyes of files.
In C you can draw something.
In C you can program interesting games.

10.2 寫入文件

10.2.1 寫入空文件


"""write_message.py"""

filename = ‘chapter_10/programming1.txt‘

with open(filename, ‘w‘) as f:
    f.write("I love programming.")

# 驗證
with open(filename) as f:
    print "The following are in the file:"
    print f.read()
The following are in the file:
I love programming.

打開文件時,可指定讀取模式(‘r‘)、寫入模式(‘w‘)、附加模式(‘a‘)或讓你能夠讀取和寫入文件的模式(‘r+‘)。如果省略了模式實參,Python將以默認的只讀模式打開文件。

如果要寫入的文件不存在,函數open()將自動創建它。然而,以寫入(‘w‘)模式打開文件時千萬要小心,因為如果指定的文件已經存在,Python將在返回文件對象前清空該文件。

註意:

Python只能將字符串寫入文本文件。要將數值數據存儲到文本文件中,必須先使用函數str()將其轉換為字符串格式。

10.2.2 寫入多行



filename = ‘chapter_10/programming2.txt‘

with open(filename, ‘w‘) as f:
    f.write("I love programming.\n")   # 插入換行符,否則這兩句會連在一起
    f.write("I love creating new games.\n")   
    
# 驗證
with open(filename) as f:
    print "The following are in the file:"
    print f.read()
The following are in the file:
I love programming.
I love creating new games.

10.2.3 附加到文件


"""
如果要給文件添加內容,而不是覆蓋原有的內容,可以附加模式打開文件,寫入到文件的行都將添加到文件末尾。
如果指定的文件不存在,Python將創建一個空文件。
"""

filename = ‘chapter_10/programming3.txt‘

with open(filename, ‘a‘) as f:
    f.write("I also love finding meaning in large dataset.\n")
    f.write("I love creating apps that can run in browser.\n")
    
# 驗證
with open(filename) as f:
    print "The following are in the file:"
    print f.read()
The following are in the file:
I love programming.
I love creating new games.
I also love finding meaning in large dataset.
I love creating apps that can run in browser.

習題:10-3 訪客


"""
編寫一個程序,提示用戶輸入其名字;用戶作出響應後,將其名字寫入到文件 guest.txt 中。 
"""

filename = "chapter_10/guest.txt"
name = raw_input("Hello!\nWhat‘s your name?\n")
with open(filename, "w") as f:
    f.write(name)
    
# 驗證
with open(filename) as f:
    print "The following are in the file:"
    print f.read()
Hello!
What‘s your name?
Dan
The following are in the file:
Dan

習題:10-4 訪客名單


"""
編寫一個 while 循環,提示用戶輸入其名字。用戶輸入其名字後,在屏幕上打印一句問候語,並將一條訪問記錄添加到文件 guest_book.txt 中。確保這個文件中的每條記錄都獨占一行。 
"""

filename = "chapter_10/guest_book.txt"
name = ""
with open(filename, "a") as f:
    while name != "quit":
        print "Enter ‘quit‘ to quit this program."
        name = raw_input("Please write down your name: ")
    
        if name == "quit":
            break
            
        line =  "Hello! " + name + "\n"
        f.write(line)
        print line
Enter ‘quit‘ to quit this program.
Please write down your name: Ann
Hello! Ann

Enter ‘quit‘ to quit this program.
Please write down your name: Bob
Hello! Bob

Enter ‘quit‘ to quit this program.
Please write down your name: Candy
Hello! Candy

Enter ‘quit‘ to quit this program.
Please write down your name: Denny
Hello! Denny

Enter ‘quit‘ to quit this program.
Please write down your name: quit

10.3 異常

10.3.1 ZeroDivisionError 異常


"""division.py"""

print 5/0
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-6-8c4a85b4b7dc> in <module>()
      2 """division.py"""
      3 
----> 4 print 5/0


ZeroDivisionError: integer division or modulo by zero

在上述traceback中,錯誤ZeroDivisionError是一個異常對象。Python無法按你的要求做時,就會創建這種對象。在這種情況下,Python將停止運行程序,並指出引發了哪種異常,而我們可根據這些信息對程序進行修改。

下面我們將告訴Python,發生這種錯誤時怎麽辦;這樣,如果再次發生這樣的錯誤,我們就有備無患了。

10.3.2 使用try-except代碼塊



"""處理ZeroDivisionError異常的try-except代碼塊類似於下面這樣: """

try:
    print 5/0
except ZeroDivisionError:
    print "You can‘t divide by zero!"
You can‘t divide by zero!

在這個示例中,try代碼塊中的代碼引發了ZeroDivisionError異常,因此Python指出了該如何解決問題的except代碼塊,並運行其中的代碼。這樣,用戶看到的是一條友好的錯誤消息,而不是traceback。

10.3.3 使用異常避免崩潰


"""
發生錯誤時,如果程序還有工作沒有完成,妥善地處理錯誤就尤其重要。這種情況經常會出現在要求用戶提供輸入的程序中;
如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入,而不至於崩潰。 
下面來創建一個只執行除法運算的簡單計算器:
"""

"""division.py"""

print "Give me two numbers, and I‘ll divide them." 
print "Enter ‘q‘ to quit."
 
while True: 
    first_number = raw_input("\nFirst number: ") 
    if first_number == ‘q‘: 
        break 
    second_number = raw_input("Second number: ") 
    if second_number == ‘q‘: 
        break 
    answer = float(first_number) / float(second_number) 
    print(answer) 
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit.

First number: 5
Second number: 0



---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-11-32d5fd3de975> in <module>()
     17     if second_number == ‘q‘:
     18         break
---> 19     answer = float(first_number) / float(second_number)
     20     print(answer)


ZeroDivisionError: float division by zero

程序崩潰可不好,但讓用戶看到traceback也不是好主意。不懂技術的用戶會被它們搞糊塗,而且如果用戶懷有惡意,他會通過traceback獲悉你不希望他知道的信息。例如,他將知道你的程序文件的名稱,還將看到部分不能正確運行的代碼。有時候,訓練有素的攻擊者可根據這些信息判斷出可對你的代碼發起什麽樣的攻擊。

10.3.4 else 代碼塊


"""
通過將可能引發錯誤的代碼放在try-except代碼塊中,可提高這個程序抵禦錯誤的能力。錯誤是執行除法運算的代碼行導致的,
因此我們需要將它放到try-except代碼塊中。這個示例還包含一個else代碼塊;依賴於try代碼塊成功執行的代碼都應放到
else代碼塊中: 
"""

print "Give me two numbers, and I‘ll divide them." 
print "Enter ‘q‘ to quit."
 
while True: 
    first_number = raw_input("\nFirst number: ") 
    if first_number == ‘q‘: 
        break 
    second_number = raw_input("Second number: ") 
    #if second_number == ‘q‘: 
         #break
    try:
        answer = float(first_number) / float(second_number) 
    except ZeroDivisionError: 
        print "You can‘t divide by 0!"
    else:
        print(answer) 
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit.

First number: 5
Second number: 0
You can‘t divide by 0!

First number: 5
Second number: 2
2.5

First number: q

try-except-else代碼塊的工作原理大致如下:Python嘗試執行try代碼塊中的代碼;except代碼塊告訴Python,如果它嘗試運行try代碼塊中的代碼時引發了指定的異常,該怎麽辦。

通過預測可能發生錯誤的代碼,可編寫健壯的程序,它們即便面臨無效數據或缺少資源,也能繼續運行,從而能夠抵禦無意的用戶錯誤和惡意的攻擊。

10.3.5 處理 FileNotFoundError 異常


"""
使用文件時,一種常見的問題是找不到文件:你要查找的文件可能在其他地方、文件名可能不正確或者這個文件根本就不存在。
對於所有這些情形,都可使用try-except代碼塊以直觀的方式進行處理。 
我們來嘗試讀取一個不存在的文件。下面的程序嘗試讀取文件alice.txt的內容,但我沒有將這個文件存儲在alice.py
所在的目錄中: 
"""

"""alice.py"""

filename = ‘alice.txt‘ 
 
with open(filename) as f_obj: 
    contents = f_obj.read() 
---------------------------------------------------------------------------

IOError                                   Traceback (most recent call last)

<ipython-input-14-69c4fe15fe12> in <module>()
     10 filename = ‘alice.txt‘
     11 
---> 12 with open(filename) as f_obj:
     13     contents = f_obj.read()


IOError: [Errno 2] No such file or directory: ‘alice.txt‘

在這個示例中,這個錯誤是函數open()導致的,因此要處理這個錯誤,必須將try語句放在包含open()的代碼行之前:

filename = ‘alice.txt‘ 
 
try: 
    with open(filename) as f_obj: 
        contents = f_obj.read() 
except IOError: 
    msg = "Sorry, the file ‘" + filename + "‘ does not exist." 
    print msg
Sorry, the file ‘alice.txt‘ does not exist.

10.3.6 分析文本


"""
下面來提取童話Alice  in  Wonderland的文本,並嘗試計算它包含多少個單詞。我們將使用方法split(),
它根據一個字符串創建一個單詞列表。
"""

filename = ‘chapter_10/alice.txt‘

try:
    with open(filename) as f:
        contents = f.read()
except IOError:
    msg = "Sorry, the file ‘" + filename + "‘ does not exist."
    print msg
else:
    # 計算文件大致包含多少個單詞
    words  = contents.split()
    num_words = len(words)
    print "The file ‘" + filename + "‘ has about " + str(num_words) + " words."
The file ‘chapter_10/alice.txt‘ has about 29461 words.

10.3.7 使用多個文件


"""
下面多分析幾本書。這樣做之前,我們先將這個程序的大部分代碼移到一個名為count_words()的函數中,
這樣對多本書進行分析時將更容易: 
"""
"""word_count.py """

def count_words(filename): 
    """計算一個文件大致包含多少個單詞""" 
    try: 
        with open(filename) as f_obj: 
            contents = f_obj.read()  
    except IOError: 
        msg = "Sorry, the file ‘" + filename + "‘ does not exist." 
        print(msg) 
    else: 
        # 計算文件大致包含多少個單詞 
        words = contents.split() 
        num_words = len(words) 
        print "The file ‘" + filename + "‘ has about " + str(num_words) +  " words."
            
filenames = [‘chapter_10/alice.txt‘, ‘chapter_10/siddhartha.txt‘, ‘chapter_10/moby_dick.txt‘, ‘chapter_10/little_women.txt‘] 
for filename in filenames: 
    count_words(filename) 
The file ‘chapter_10/alice.txt‘ has about 29461 words.
Sorry, the file ‘chapter_10/siddhartha.txt‘ does not exist.
The file ‘chapter_10/moby_dick.txt‘ has about 215136 words.
The file ‘chapter_10/little_women.txt‘ has about 189079 words.

文件siddhartha.txt不存在,但這絲毫不影響這個程序處理其他文件。
在這個示例中,使用try-except代碼塊提供了兩個重要的優點:

  • 避免讓用戶看到traceback;
  • 讓程序能夠繼續分析能夠找到的其他文件。

如果不捕獲因找不到siddhartha.txt而引發IOError異常,用戶將看到完整的traceback,而程序將在嘗試分析Siddhartha後停止運行——根本不分析Moby Dick和Little Women。

10.3.8 失敗時一聲不吭


"""
在前一個示例中,我們告訴用戶有一個文件找不到。但並非每次捕獲到異常時都需要告訴用戶,有時候你希望程序在發生異常時
一聲不吭,就像什麽都沒有發生一樣繼續運行。要讓程序在失敗時一聲不吭,可像通常那樣編寫try代碼塊,但在except代碼塊
中明確地告訴Python什麽都不要做。Python有一個pass語句,可在代碼塊中使用它來讓Python什麽都不要做: 
"""

def count_words(filename): 
    """計算一個文件大致包含多少個單詞""" 
    try: 
        with open(filename) as f_obj: 
            contents = f_obj.read()  
    except IOError: 
         pass 
    else: 
        # 計算文件大致包含多少個單詞 
        words = contents.split() 
        num_words = len(words) 
        print "The file ‘" + filename + "‘ has about " + str(num_words) +  " words."
    

filenames = [‘chapter_10/alice.txt‘, ‘chapter_10/siddhartha.txt‘, ‘chapter_10/moby_dick.txt‘, ‘chapter_10/little_women.txt‘] 
for filename in filenames: 
    count_words(filename) 
The file ‘chapter_10/alice.txt‘ has about 29461 words.
The file ‘chapter_10/moby_dick.txt‘ has about 215136 words.
The file ‘chapter_10/little_women.txt‘ has about 189079 words.

pass語句還充當了占位符,它提醒你在程序的某個地方什麽都沒有做,並且以後也許要在這裏做些什麽。

例如,在這個程序中,我們可能決定將找不到的文件的名稱寫入到文件missing_files.txt中。用戶看不到這個文件,但我們可以讀取這個文件,進而處理所有文件找不到的問題。

"""
10-6  加法運算:提示用戶提供數值輸入時,常出現的一個問題是,用戶提供的是文本而不是數字。在這種情況下,當你嘗試
將輸入轉換為整數時,將引發 TypeError 異常。編寫一個程序,提示用戶輸入兩個數字,再將它們相加並打印結果。在用戶
輸入的任何一個值不是數字時都捕獲 TypeError 異常,並打印一條友好的錯誤消息。對你編寫的程序進行測試:先輸入兩個
數字,再輸入一些文本而不是數字。 
"""


print "Give me two numbers, and I‘ll add them."

num_1 = raw_input("First number: ")
num_2 = raw_input("Second number: ")

try:
    sum = float(num_1) + float(num_2)
except ValueError:
    print "\nYou didn‘t give me two NUMBERS!"
else:
    print "\nThe answer is: " + str(sum)
  
   
Give me two numbers, and I‘ll add them.
First number: 1.2
Second number: 4.3

The answer is: 5.5
"""
10-7  加法計算器:將你為完成練習 10-6 而編寫的代碼放在一個 while 循環中,讓用戶犯錯(輸入的是文本而不是數字)
後能夠繼續輸入數字。 
"""


print "Give me two numbers, and I‘ll add them."
print "Enter ‘q‘ to quit."


check = True
while check:
    num_1 = raw_input("\nFirst number: ")
    if num_1 == "q":
        break
    num_2 = raw_input("Second number: ")
    if num_2 == "q":
        break
    try:
        sum = float(num_1) + float(num_2)
    except ValueError:
        print "\nYou didn‘t give me two NUMBERS!"
        continue
    else:
        print "\nThe answer is: " + str(sum)
        check = False
   
Give me two numbers, and I‘ll add them.
Enter ‘q‘ to quit.

First number: d
Second number: g

You didn‘t give me two NUMBERS!

First number: 3
Second number: o

You didn‘t give me two NUMBERS!

First number: 7
Second number: 6

The answer is: 13.0
"""
10-8 貓和狗:
創建兩個文件 cats.txt 和 dogs.txt,在第一個文件中至少存儲三只貓的名字,在第二個文件中至少存儲三條狗的名字。
編寫一個程序,嘗試讀取這些文件,並將其內容打印到屏幕上。將這些代碼放在一個 try-except 代碼塊中,以便在文件不
存在時捕獲 FileNotFound 錯誤,並打印一條友好的消息。將其中一個文件移到另一個地方,並確認 except 代碼塊中的
代碼將正確地執行。 
"""
catsname = "chapter_10/cats.txt"
dogsname = "chapter_10/dogs.txt"

def readfile(filename):
    
        try:
            with open(filename) as f:
                print filename.upper()
                print f.read()
        except IOError:
            print "The ‘" + filename + "‘ does not exist!"
            
            
readfile(catsname)   
readfile(dogsname)   
The ‘chapter_10/cats.txt‘ does not exist!
The ‘chapter_10/dogs.txt‘ does not exist!
"""
10-9 沈默的貓和狗:修改你在練習 10-8 中編寫的 except 代碼塊,讓程序在文件不存在時一言不發。 
"""
catsname = "chapter_10/cats.txt"
dogsname = "chapter_10/dogs.txt"

def readfile(filename):
    
        try:
            with open(filename) as f:
                print filename.upper()
                print f.read()
        except IOError:
            pass
        
        print "Complete."   
            
readfile(catsname)   
readfile(dogsname)   
Complete.
Complete.

10.4 存儲數據

模塊json讓你能夠將簡單的Python數據結構轉儲到文件中,並在程序再次運行時加載該文件中的數據。你還可以使用json在Python程序之間分享數據。

更重要的是,JSON數據格式並非Python專用的,這讓你能夠將以JSON格式存儲的數據與使用其他編程語言的人分享。這是一種輕便格式,很有用,也易於學習。

10.4.1 使用 json.dump()和 json.load()

函數json.dump()接受兩個實參:要存儲的數據以及可用於存儲數據的文件對象。


"""number_write.py"""

import json  # 導入json模塊

numbers = [2, 3, 5, 7, 11, 13]

filename = ‘chapter_10/numbers.json‘
with open(filename, ‘w‘) as f:
    json.dump(numbers, f)
"""number_reader.py"""

filename = ‘chapter_10/numbers.json‘
with open(filename) as f:
    numbers = json.load(f)
    
print numbers    
[2, 3, 5, 7, 11, 13]

10.4.2 保存和讀取用戶生成的數據

"""存儲用戶的名字"""
"""remember_me.py"""

import json

username = raw_input("What‘s your name? ")

filename = "chapter_10/username.json"
with open(filename, "w") as f:
    json.dump(username, f)
    print "We‘ll remember you when you come back, " + username + "!"
What‘s your name? Eric
We‘ll remember you when you come back, Eric!
"""再編寫一個程序,向其名字被存儲的用戶發出問候: """
"""greet_user.py """

import json
filename = "chapter_10/username.json"

with open(filename) as f:
    username = json.load(f)
    print "Welcome back, " + username + "!"
Welcome back, Eric!
"""合並兩個文件"""
"""remember_me.py"""

import json

# 如果以前存儲了用戶名,就加載它
# 否則,就提示用戶輸入用戶名並存儲它

filename = "chapter_10/username.json"

try:
    with open(filename) as f:
        username = json.load(f)
        
except IOError:
    username = raw_input("What‘s  your name? ")
    with open(filename, "w") as f:
        json.dump(username, f)
        print "We‘ll remember you when you come back, " + username + "!"
else:
    print "Welcome back, " + username + "!"
Welcome back, Eric!

10.4.3 重構

經常會遇到這樣的情況:代碼能夠正確地運行,但可做進一步的改進——將代碼劃分為一系列完成具體工作的函數。這樣的過程
被稱為重構。重構讓代碼更清晰、更易於理解、更容易擴展。
要重構remember_me.py,可將其大部分邏輯放到一個或多個函數中。remember_me.py的重點是問候用戶,因此我們將其所
有代碼都放到一個名為greet_user()的函數中:

"""remrember_me.py"""

def greet_user():
    """問候用戶,並指出其名字"""
    filename = "chapter_10/username.json"
    try:
        with open(filename) as f:
            username = json.load(f)  
    except IOError:
        username = raw_input("What‘s  your name? ")
        with open(filename, "w") as f:
            json.dump(username, f)
            print "We‘ll remember you when you come back, " + username + "!"
    else:
        print "Welcome back, " + username + "!"
        
        
greet_user()        
Welcome back, Eric!
"""
重構greet_user(),讓它不執行這麽多任務。先將獲取存儲的用戶名的代碼移到另一個函數中 
"""

import json

def get_stored_username():
     """如果存儲了用戶名,就獲取它""" 
    filename = "chapter_10/username.json"
    try:
        with open(filename) as f:
            username = json.load(f)  
    except IOError:
        return None
    else:
        return username
    
def greet_user():
    username = get_stored_username()
    if username:
        print "Welcome back, " + username + "!"
    else:
        username = raw_input("What‘s  your name? ")
        with open(filename, "w") as f:
            json.dump(username, f)
            print "We‘ll remember you when you come back, " + username + "!"
        
greet_user()    
Welcome back, Eric!
"""
將greet_user()中的另一個代碼塊提取出來:將沒有存儲用戶名時提示用戶輸入的代碼放在一個獨立的函數中: 
"""

import json



def get_stored_username():
        
    try:
        with open(filename) as f:
            username = json.load(f)  
    except IOError:
        return None
    else:
        return username
    
def get_new_username(): 
    """提示用戶輸入用戶名""" 
    username = raw_input("What is your name? ") 
    filename = "chapter_10/username.json"
    with open(filename, ‘w‘) as f: 
        json.dump(username, f) 
    return username 

def greet_user(): 
    
    username = get_stored_username() 
    if username: 
        print "Welcome back, " + username + "!"
    else: 
        username = get_new_username()   
        print "We‘ll remember you when you come back, " + username + "!" 

        
greet_user() 
    
Welcome back, Eric!

在remember_me.py的這個最終版本中,每個函數都執行單一而清晰的任務。

我們調用greet_user(),它打印一條合適的消息:要麽歡迎老用戶回來,要麽問候新用戶。

為此,它首先調用get_stored_username(),這個函數只負責獲取存儲的用戶名(如果存儲了的話),再在必要時調用get_new_username(),這個函數只負責獲取並存儲新用戶的用戶名。要編寫出清晰而易於維護和擴展的代碼,這種劃分工作必不可少。

10-11 喜歡的數字: 編寫一個程序,提示用戶輸入他喜歡的數字,並使用json.dump()將這個數字存儲到文件中。
再編寫一個程序,從文件中讀取這個值,並打印消息“I know your favorite number! It’s _____.”。

import json

filename = "chapter_10/favorite_number.json"
num = raw_input("What‘s your favorite number? ")
with open(filename, "w") as f:
    json.dump(num, f)
    
    
with open(filename) as f:
    answer = json.load(f)
    
print "I know your favorite number! It‘s " + str(answer) + "."  
What‘s your favorite number? 20
I know your favorite number! It‘s 20.

10-12 記住喜歡的數字:將練習 10-11 中的兩個程序合而為一。如果存儲了用戶喜歡的數字,就向用戶顯示它,
否則提示用戶輸入他喜歡的數字並將其存儲到文件中。運行這個程序兩次,看看它是否像預期的那樣工作。

import json

filename = "chapter_10/favorite_number.json"

with open(filename) as f:
    
    try:
        answer = json.load(f)   
        print "I know your favorite number! It‘s " + str(answer) + "." 
    except ValueError:
        print "Oh, I don‘t know your favorite number."
        num = raw_input("So what‘s your favorite number? ")
        with open(filename, "w") as f:
            json.dump(num, f)
    
    
   
I know your favorite number! It‘s 20.

10-13 驗證用戶:最後一個 remember_me.py 版本假設用戶要麽已輸入其用戶名,要麽是首次運行該程序。我們應修改
這個程序,以應對這樣的情形:當前和最後一次運行該程序的用戶並非同一個人。
為此,在 greet_user()中打印歡迎用戶回來的消息前,先詢問他用戶名是否是對的。
如果不對,就調用 get_new_username()讓用戶輸入正確的用戶名。


import json

def get_stored_username(): 
    filename = "chapter_10/username.json"
    try:
        with open(filename) as f:
            username = json.load(f)  
    except IOError:
        return None
    else:
        return username
    
def get_new_username(): 
    """提示用戶輸入用戶名""" 
    username = raw_input("What is your name ? ") 
    filename = "chapter_10/username.json"
    with open(filename, ‘w‘) as f: 
        json.dump(username, f) 
    return username 

def greet_user():     
    username = get_stored_username() 
    if username: 
        check = raw_input("Are you " + username + " ? y/s ")
        if check == "y":
            print "Welcome back, " + username + "!"
        if check == "n":
            print "I‘s sorry."
            username = get_new_username()
            print "We‘ll remember you when you come back, " + username + "!" 
    else: 
        username = get_new_username()   
        print "We‘ll remember you when you come back, " + username + "!" 

        
greet_user() 
    
    Are you Ada ? y/s n
    I‘m sorry.
    What is your name ? Eric
    We‘ll remember you when you come back, Eric!

《Python編程:從入門到實踐》第10章 筆記