相關閱讀:跟上時代的步伐,學一波Python(一)
6. 字典
6.1 一個簡單的字典
alien_0 = {'color': 'green', 'points': 5}
print(alien_0['color'])
print(alien_0['points'])
字典alien_0 存儲了外星人的顏色和點數。使用兩條print 語句來訪問并打印這些信息,如下所示:
green
5
6.2 使用字典
在Python中,字典 是一系列鍵—值對 。每個鍵 都與一個值相關聯,你可以使用鍵來訪問與之相關聯的值。與鍵相關聯的值可以是數字、字符串、列表乃至字典。事實上,可將任何Python對象用作字典中的值。
在Python中,字典用放在花括號{} 中的一系列鍵—值對表示,如前面的示例所示:
alien_0 = {'color': 'green', 'points': 5}
6.2.1 訪問字典中的值
要獲取與鍵相關聯的值,可依次指定字典名和放在方括號內的鍵,如下所示:
alien_0 = {'color': 'green'}
print(alien_0['color'])
這將返回字典alien_0 中與鍵'color' 相關聯的值:
green
6.2.2 添加鍵—值對
字典是一種動態結構,可隨時在其中添加鍵—值對。要添加鍵—值對,可依次指定字典名、用方括號括起的鍵和相關聯的值。
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)
輸出結果:
{'color': 'green', 'points': 5}
{'color': 'green', 'points': 5, 'y_position': 25, 'x_position': 0}
6.2.3 修改字典中的值
要修改字典中的值,可依次指定字典名、用方括號括起的鍵以及與該鍵相關聯的新值。
alien_0 = {'color': 'green'}
print("The alien is " + alien_0['color'] + ".")
alien_0['color'] = 'yellow'
print("The alien is now " + alien_0['color'] + ".")
輸出結果:
The alien is green.
The alien is now yellow.
6.2.4 刪除鍵—值對
對于字典中不再需要的信息,可使用del 語句將相應的鍵—值對徹底刪除。使用del 語句時,必須指定字典名和要刪除的鍵。
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
del alien_0['points']
print(alien_0)
輸出結果:
{'color': 'green', 'points': 5}
{'color': 'green'}
6.3 遍歷字典
一個Python字典可能只包含幾個鍵—值對,也可能包含數百萬個鍵—值對。鑒于字典可能包含大量的數據,Python支持對字典遍歷。字典可用于以各種方式存儲信息,因此有多種遍歷字典的方式:可遍歷字典的所有鍵—值對、鍵或值。
6.3.1 遍歷所有的鍵—值對
user_0 = {
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}
for key, value in user_0.items():
print("\nKey: " + key)
print("Value: " + value)
輸出結果:
Key: last
Value: fermi
Key: first
Value: enrico
Key: username
Value: efermi
6.3.2 遍歷字典中所有鍵
在不需要使用字典中的值時,方法keys() 很有用。下面來遍歷字典favorite_languages ,并將每個被調查者的名字都打印出來:
favorite_languages =
{ 'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python', }
for name in favorite_languages.keys():
print(name.title())
輸出結果:
Jen
Sarah
Phil
Edward
6.3.3 按順序遍歷字典中的所有鍵
字典總是明確地記錄鍵和值之間的關聯關系,但獲取字典的元素時,獲取順序是不可預測的。這不是問題,因為通常你想要的只是獲取與鍵相關聯的正確的值。 要以特定的順序返回元素,一種辦法是在for 循環中對返回的鍵進行排序。為此,可使用函數sorted() 來獲得按特定順序排列的鍵列表的副本:
favorite_languages =
{ 'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python', }
for name in sorted(favorite_languages.keys()):
print(name.title())
輸出結果:
Edward
Jen
Phil
Sarah
6.3.4 遍歷字典中的所有值
favorite_languages =
{ 'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python', }
print("The following languages have been mentioned:")
for language in favorite_languages.values():
print(language.title())
這條for 語句提取字典中的每個值,并將它們依次存儲到變量language 中。通過打印這些值,就獲得了一個列表,其中包含被調查者選擇的各種語言:
The following languages have been mentioned:
Python
C
Python
Ruby
7. 函數
7.1 定義函數
一個簡單的函數
def greet_user():
"""顯示簡單的問候語"""
print("Hello!")
greet_user()
輸出結果:
Hello!
7.1.1 向函數傳遞信息
只需稍作修改,就可以讓函數greet_user() 不僅向用戶顯示Hello! ,還將用戶的名字用作抬頭。為此,可在函數定義def greet_user() 的括號內添加username 。通 過在這里添加username ,就可讓函數接受你給username 指定的任何值。現在,這個函數要求你調用它時給username 指定一個值。調用greet_user() 時,可將一個名字 傳遞給它,如下所示:
def greet_user(username):
"""顯示簡單的問候語"""
print("Hello, " + username.title() + "!")
greet_user('jesse')
代碼greet_user('jesse') 調用函數greet_user() ,并向它提供執行print 語句所需的信息。這個函數接受你傳遞給它的名字,并向這個人發出問候:
Hello, Jesse!
7.1.2 實參和形參
前面定義函數greet_user() 時,要求給變量username 指定一個值。調用這個函數并提供這種信息(人名)時,它將打印相應的問候語。
在函數greet_user() 的定義中,變量username 是一個形參 ——函數完成其工作所需的一項信息。在代碼greet_user('jesse') 中,值'jesse' 是一個實參 。實參是 調用函數時傳遞給函數的信息。我們調用函數時,將要讓函數使用的信息放在括號內。在greet_user('jesse') 中,將實參'jesse' 傳遞給了函數greet_user() ,這個 值被存儲在形參username 中。
7.2 傳遞實參
鑒于函數定義中可能包含多個形參,因此函數調用中也可能包含多個實參。向函數傳遞實參的方式很多,可使用位置實參 ,這要求實參的順序與形參的順序相同;也可使用關鍵字實參 ,其中每個實參都由變量名和值組成;還可使用列表和字典。下面來依次介紹這些方式。
7.2.1 位置實參
參數傳遞時,需要按照形參的位置順序來傳遞,如下例所示,animal的type和name需按順序傳入
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
輸出結果:
I have a harry.
My harry's name is Hamster.
7.2.2 關鍵字實參
關鍵字實參 是傳遞給函數的名稱—值對。你直接在實參中將名稱和值關聯起來了,因此向函數傳遞實參時不會混淆(不會得到名為Hamster的harry這樣的結果)。關鍵字實參讓你無需考慮函數調用中的實參順序,還清楚地指出了函數調用中各個值的用途。
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(animal_type='hamster', pet_name='harry')
輸出結果:
I have a harry.
My harry's name is Hamster.
7.2.3 默認值
編寫函數時,可給每個形參指定默認值 。在調用函數中給形參提供了實參時,Python將使用指定的實參值;否則,將使用形參的默認值。因此,給形參指定默認值后,可在函數調用中省略相應的實參。使用默認值可簡化函數調用,還可清楚地指出函數的典型用法。
def describe_pet(pet_name, animal_type='dog'):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(pet_name='willie')
這里修改了函數describe_pet() 的定義,在其中給形參animal_type 指定了默認值'dog' 。這樣,調用這個函數時,如果沒有給animal_type 指定值,Python將把這個 形參設置為'dog' :
I have a dog.
My dog's name is Willie.
7.3 返回值
函數并非總是直接顯示輸出,相反,它可以處理一些數據,并返回一個或一組值。函數返回的值被稱為返回值 。在函數中,可使用return 語句將值返回到調用函數的代碼行。
7.3.1 返回簡單值
下面來看一個函數,它接受名和姓并返回整潔的姓名:
def get_formatted_name(first_name, last_name):
"""返回整潔的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix') print(musician)
輸出結果:
Jimi Hendrix
7.3.2 返回字典
函數可返回任何類型的值,包括列表和字典等較復雜的數據結構。例如,下面的函數接受姓名的組成部分,并返回一個表示人的字典:
def build_person(first_name, last_name):
"""返回一個字典,其中包含有關一個人的信息"""
person = {'first': first_name, 'last': last_name}
return person
musician = build_person('jimi', 'hendrix')
print(musician)
輸出結果:
{'first': 'jimi', 'last': 'hendrix'}
7.3.3 結合使用函數和while循環
可將函數同本書前面介紹的任何Python結構結合起來使用。例如,下面將結合使用函數get_formatted_name() 和while 循環,以更正規的方式問候用戶。下面嘗試使用名
def get_formatted_name(first_name, last_name):
"""返回整潔的姓名"""
full_name = first_name + ' ' + last_name return full_name.title()
while True:
print("\nPlease tell me your name:")
print("(enter 'q' at any time to quit)")
f_name = input("First name: ")
if f_name == 'q':
break
l_name = input("Last name: ")
if l_name == 'q':
break
formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
我們添加了一條消息來告訴用戶如何退出,然后在每次提示用戶輸入時,都檢查他輸入的是否是退出值,如果是,就退出循環。現在,這個程序將不斷地問候,直到用戶輸入的姓或名為'q' 為止:
Please tell me your name:
(enter 'q' at any time to quit)
First name: eric
Last name: matthes
Hello, Eric Matthes!
Please tell me your name:
(enter 'q' at any time to quit)
First name: q
7.4 傳遞列表
def greet_users(names):
"""向列表中的每位用戶都發出簡單的問候"""
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
輸出結果:
Hello, Hannah!
Hello, Ty!
Hello, Margot!
7.5 將函數存在模塊中
函數的優點之一是,使用它們可將代碼塊與主程序分離。通過給函數指定描述性名稱,可讓主程序容易理解得多。你還可以更進一步,將函數存儲在被稱為模塊 的獨立文件中,再將模塊導入 到主程序中。import 語句允許在當前運行的程序文件中使用模塊中的代碼。 通過將函數存儲在獨立的文件中,可隱藏程序代碼的細節,將重點放在程序的高層邏輯上。這還能讓你在眾多不同的程序中重用函數。將函數存儲在獨立文件中后,可與其他程序員共享這些文件而不是整個程序。知道如何導入函數還能讓你使用其他程序員編寫的函數庫。
7.5.1 導入整個模塊
要讓函數是可導入的,得先創建模塊。模塊 是擴展名為.py的文件,包含要導入到程序中的代碼。下面來創建一個包含函數make_pizza() 的模塊。為此,我們將文件pizza.py中除函數make_pizza() 之外的其他代碼都刪除:
pizza.py
def make_pizza(size, *toppings):
"""概述要制作的比薩"""
print("\nMaking a " + str(size) +"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)
接下來,我們在pizza.py所在的目錄中創建另一個名為making_pizzas.py的文件,這個文件導入剛創建的模塊,再調用make_pizza() 兩次:
making_pizzas.py
import pizza
pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Python讀取這個文件時,代碼行import pizza 讓Python打開文件pizza.py,并將其中的所有函數都復制到這個程序中。你看不到復制的代碼,因為這個程序運行時,Python在幕 后復制這些代碼。你只需知道,在making_pizzas.py中,可以使用pizza.py中定義的所有函數。
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
7.6.2 導入特定的函數
你還可以導入模塊中的特定函數,這種導入方法的語法如下:
from module_name import function_name
通過用逗號分隔函數名,可根據需要從模塊中導入任意數量的函數:
from module_name import function_0, function_1, function_2
7.6.3 使用as給函數指定別名
如果要導入的函數的名稱可能與程序中現有的名稱沖突,或者函數的名稱太長,可指定簡短而獨一無二的別名 ——函數的另一個名稱,類似于外號。要給函數指定這種特殊外 號,需要在導入它時這樣做。
下面給函數make_pizza() 指定了別名mp() 。這是在import 語句中使用make_pizza as mp 實現的,關鍵字as 將函數重命名為你提供的別名:
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
上面的import 語句將函數make_pizza() 重命名為mp() ;在這個程序中,每當需要調用make_pizza() 時,都可簡寫成mp() ,而Python將運行make_pizza() 中的代 碼,這可避免與這個程序可能包含的函數make_pizza() 混淆。
指定別名的通用語法如下:
from module_name import function_name as fn
7.6.4 使用as給模塊指定別名
你還可以給模塊指定別名。通過給模塊指定簡短的別名(如給模塊pizza 指定別名p ),讓你能夠更輕松地調用模塊中的函數。相比于pizza.make_pizza(),p.make_pizza() 更為簡潔:
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
給模塊指定別名的通用語法如下:
import module_name as mn
7.6.5 導入模塊中的所有函數
使用星號(* )運算符可讓Python導入模塊中的所有函數:
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
8. 類
8.1 創建和使用類
使用類幾乎可以模擬任何東西。下面來編寫一個表示小狗的簡單類Dog ——它表示的不是特定的小狗,而是任何小狗。對于大多數寵物狗,我們都知道些什么呢?它們都有名字 和年齡;我們還知道,大多數小狗還會蹲下和打滾。由于大多數小狗都具備上述兩項信息(名字和年齡)和兩種行為(蹲下和打滾),我們的Dog 類將包含它們。這個類讓 Python知道如何創建表示小狗的對象。編寫這個類后,我們將使用它來創建表示特定小狗的實例。
8.1.1 創建Dog類
class Dog():
"""一次模擬小狗的簡單嘗試"""
def __init__(self, name, age):
"""初始化屬性name和age"""
self.name = name self.age = age
def sit(self):
"""模擬小狗被命令時蹲下"""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""模擬小狗被命令時打滾"""
print(self.name.title() + " rolled over!")
8.1.2 根據類創建實例
class Dog():
--snip--
my_dog = Dog('willie', 6)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
輸出結果
My dog's name is Willie.
My dog is 6 years old.
調用方法
class Dog():
--snip--
my_dog = Dog('willie', 6)
my_dog.sit()
my_dog.roll_over()
輸出結果:
Willie is now sitting.
Willie rolled over!
創建多個實例
class Dog():
--snip--
my_dog = Dog('willie', 6)
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.") my_dog.sit()
print("\nYour dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.") your_dog.sit()
輸出結果:
My dog's name is Willie.
My dog is 6 years old.
Willie is now sitting.
Your dog's name is Lucy.
Your dog is 3 years old.
Lucy is now sitting.
8.2 使用類和實例
8.2.1 Car類
class Car():
"""一次模擬汽車的簡單嘗試"""
def __init__(self, make, model, year): """初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year
def get_descriptive_name(self):
"""返回整潔的描述性信息"""
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
輸出結果:
2016 Audi A4
8.2.2 給屬性默認值
類中的每個屬性都必須有初始值,哪怕這個值是0或空字符串。在有些情況下,如設置默認值時,在方法init() 內指定這種初始值是可行的;如果你對某個屬性這樣做 了,就無需包含為它提供初始值的形參。
下面來添加一個名為odometer_reading 的屬性,其初始值總是為0。我們還添加了一個名為read_odometer() 的方法,用于讀取汽車的里程表:
class Car():
def __init__(self, make, model, year):
"""初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year self.odometer_reading = 0
def get_descriptive_name(self):
--snip--
def read_odometer(self):
"""打印一條指出汽車里程的消息"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
輸出結果:
2016 Audi A4
This car has 0 miles on it.
8.2.3 修改屬性的值
1.直接修改屬性的值
class Car():
--snip--
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
輸出結果:
2016 Audi A4
This car has 23 miles on it.
2.通過方法修改屬性的值
class Car():
--snip--
def update_odometer(self, mileage):
"""將里程表讀數設置為指定的值"""
self.odometer_reading = mileage
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.update_odometer(23)
my_new_car.read_odometer()
輸出結果:
2016 Audi A4
This car has 23 miles on it.
3.通過方法對屬性的值進行遞增
有時候需要將屬性值遞增特定的量,而不是將其設置為全新的值。假設我們購買了一輛二手車,且從購買到登記期間增加了100英里的里程,下面的方法讓我們能夠傳遞這個增量,并相應地增加里程表讀數:
class Car():
--snip--
def update_odometer(self, mileage):
--snip--
def increment_odometer(self, miles):
"""將里程表讀數增加指定的量"""
self.odometer_reading += miles
my_used_car = Car('subaru', 'outback', 2013)
print(my_used_car.get_descriptive_name())
my_used_car.update_odometer(23500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()
輸出結果:
2013 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.
8.3 繼承
編寫類時,并非總是要從空白開始。如果你要編寫的類是另一個現成類的特殊版本,可使用繼承 。一個類繼承 另一個類時,它將自動獲得另一個類的所有屬性和方法;原有的類稱為父類 ,而新類稱為子類 。子類繼承了其父類的所有屬性和方法,同時還可以定義自己的屬性和方法。
8.3.1 子類的方法init()
創建子類的實例時,Python首先需要完成的任務是給父類的所有屬性賦值。為此,子類的方法init() 需要父類施以援手。
electric_car.py
class Car():
"""一次模擬汽車的簡單嘗試"""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
def read_odometer(self):
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
self.odometer_reading += miles
class ElectricCar(Car):
"""電動汽車的獨特之處"""
def __init__(self, make, model, year):
"""初始化父類的屬性"""
super().__init__(make, model, year)
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
輸出結果:
2016 Tesla Model S
8.3.2 Python2.7中的繼承
在Python 2.7中,繼承語法稍有不同,ElectricCar 類的定義類似于下面這樣:
class Car(object):
def __init__(self, make, model, year):
--snip--
class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)
--snip--
8.3.3 給子類定義屬性和方法
讓一個類繼承另一個類后,可添加區分子類和父類所需的新屬性和方法。 下面來添加一個電動汽車特有的屬性(電瓶),以及一個描述該屬性的方法。我們將存儲電瓶容量,并編寫一個打印電瓶描述的方法:
class Car():
--snip--
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def__init__(self, make, model, year):
"""電動汽車的獨特之處 初始化父類的屬性,再初始化電動汽車特有的屬性 """
super().__init__(make, model, year)
self.battery_size = 70
def describe_battery(self):
"""打印一條描述電瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
輸出結果:
2016 Tesla Model S
This car has a 70-kWh battery.
8.3.4 重寫父類的方法
對于父類的方法,只要它不符合子類模擬的實物的行為,都可對其進行重寫。為此,可在子類中定義一個這樣的方法,即它與要重寫的父類方法同名。這樣,Python將不會考慮這個父類方法,而只關注你在子類中定義的相應方法。
def ElectricCar(Car):
--snip--
def fill_gas_tank():
"""電動汽車沒有油箱"""
print("This car doesn't need a gas tank!")
8.3.5 將實例用作屬性
使用代碼模擬實物時,你可能會發現自己給類添加的細節越來越多:屬性和方法清單以及文件都越來越長。在這種情況下,可能需要將類的一部分作為一個獨立的類提取出來。你可以將大型類拆分成多個協同工作的小類。
例如,不斷給ElectricCar 類添加細節時,我們可能會發現其中包含很多專門針對汽車電瓶的屬性和方法。在這種情況下,我們可將這些屬性和方法提取出來,放到另一個名 為Battery 的類中,并將一個Battery 實例用作ElectricCar 類的一個屬性:
class Car():
--snip--
class Battery():
"""一次模擬電動汽車電瓶的簡單嘗試"""
def __init__(self, battery_size=70):
"""初始化電瓶的屬性"""
self.battery_size = battery_size
def describe_battery(self):
"""打印一條描述電瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
class ElectricCar(Car):
"""電動汽車的獨特之處"""
def__init__(self, make, model, year):
""" 初始化父類的屬性,再初始化電動汽車特有的屬性 """
super().__init__(make, model, year)
self.battery = Battery()
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
輸出結果:
my_tesla.battery.describe_battery()
2016 Tesla Model S
This car has a 70-kWh battery
8.4 導入類
隨著你不斷地給類添加功能,文件可能變得很長,即便你妥善地使用了繼承亦如此。為遵循Python的總體理念,應讓文件盡可能整潔。為在這方面提供幫助,Python允許你將類存儲在模塊中,然后在主程序中導入所需的模塊。
8.4.1 導入單個類
car.py
"""一個可用于表示汽車的類"""
class Car():
"""一次模擬汽車的簡單嘗試"""
def __init__(self, make, model, year):
"""初始化描述汽車的屬性"""
self.make = make
self.model = model
self.year = year self.odometer_reading = 0
def get_descriptive_name(self):
"""返回整潔的描述性名稱"""
long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title()
def read_odometer(self):
"""打印一條消息,指出汽車的里程"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mileage):
"""
將里程表讀數設置為指定的值
拒絕將里程表往回撥
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""將里程表讀數增加指定的量"""
self.odometer_reading += miles
下面來創建另一個文件——my_car.py,在其中導入Car 類并創建其實例:
from car import Car
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23 my_new_car.read_odometer()
輸出結果:
2016 Audi A4
This car has 23 miles on it.
8.4.2 在一個模塊中存儲多個類
雖然同一個模塊中的類之間應存在某種相關性,但可根據需要在一個模塊中存儲任意數量的類。類Battery 和ElectricCar 都可幫助模擬汽車,因此下面將它們都加入模塊car.py中:
car.py
"""一組用于表示燃油汽車和電動汽車的類"""
class Car():
--snip--
class Battery():
"""一次模擬電動汽車電瓶的簡單嘗試"""
def __init__(self, battery_size=60):
"""初始化電瓶的屬性"""
self.battery_size = battery_size
def describe_battery(self):
"""打印一條描述電瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
def get_range(self):
"""打印一條描述電瓶續航里程的消息"""
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270
message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)
class ElectricCar(Car):
"""模擬電動汽車的獨特之處"""
def __init__(self, make, model, year):
"""初始化父類的屬性,再初始化電動汽車特有的屬性"""
super().__init__(make, model, year)
self.battery = Battery()
現在,可以新建一個名為my_electric_car.py的文件,導入ElectricCar 類,并創建一輛電動汽車了:
my_electric_car.py
from car import ElectricCar
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
輸出結果:
2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
8.4.3 從一個模塊中導入多個類
my_cars.py
from car import Car, ElectricCar
my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
輸出結果:
2016 Volkswagen Beetle
2016 Tesla Roadster
8.4.4 導入整個模塊
你還可以導入整個模塊,再使用句點表示法訪問需要的類。這種導入方法很簡單,代碼也易于閱讀。由于創建類實例的代碼都包含模塊名,因此不會與當前文件使用的任何名稱發生沖突。
my_cars.py
import car
my_beetle = car.Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
my_tesla = car.ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
9. 文件和異常
9.1 從文件中讀取數據
文本文件可存儲的數據量多得難以置信:天氣數據、交通數據、社會經濟數據、文學作品等。每當需要分析或修改存儲在文件中的信息時,讀取文件都很有用,對數據分析應用程序來說尤其如此。例如,你可以編寫一個這樣的程序:讀取一個文本文件的內容,重新設置這些數據的格式并將其寫入文件,讓瀏覽器能夠顯示這些內容。
9.1.1 讀取整個文件
要讀取文件,需要一個包含幾行文本的文件。下面首先來創建一個文件,它包含精確到小數點后30位的圓周率值,且在小數點后每10位處都換行:
pi_digits.txt
3.1415926535
8979323846
2643383279
file_reader.py
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
輸出結果:
3.1415926535
8979323846
2643383279
相比于原始文件,該輸出唯一不同的地方是末尾多了一個空行。為何會多出這個空行呢?因為read() 到達文件末尾時返回一個空字符串,而將這個空字符串顯示出來時就是一 個空行。要刪除多出來的空行,可在print 語句中使用rstrip() :
with open('pi_digits.txt') as file_object: contents = file_object.read()
print(contents.rstrip())
前面說過,Python方法rstrip() 刪除(剝除)字符串末尾的空白。現在,輸出與原始文件的內容完全相同:
3.1415926535
8979323846
2643383279
9.1.2 文件路徑
當你將類似pi_digits.txt這樣的簡單文件名傳遞給函數open() 時,Python將在當前執行的文件(即.py程序文件)所在的目錄中查找文件。
根據你組織文件的方式,有時可能要打開不在程序文件所屬目錄中的文件。例如,你可能將程序文件存儲在了文件夾python_work中,而在文件夾python_work中,有一個名為 text_files的文件夾,用于存儲程序文件操作的文本文件。雖然文件夾text_files包含在文件夾python_work中,但僅向open() 傳遞位于該文件夾中的文件的名稱也不可行,因為Python 只在文件夾python_work中查找,而不會在其子文件夾text_files中查找。要讓Python打開不與程序文件位于同一個目錄中的文件,需要提供文件路徑 ,它讓Python到系統的特定位置 去查找。
由于文件夾text_files位于文件夾python_work中,因此可使用相對文件路 徑來打開該文件夾中的文件。相對文件路徑讓Python到指定的位置去查找,而該位置是相對于當前運行的程 序所在目錄的。在Linux和OS X中,你可以這樣編寫代碼:
with open('text_files/filename.txt') as file_object:
這行代碼讓Python到文件夾python_work下的文件夾text_files中去查找指定的.txt文件。在Windows系統中,在文件路徑中使用反斜杠(\ )而不是斜杠(/ ):
with open('text_files\filename.txt') as file_object:
你還可以將文件在計算機中的準確位置告訴Python,這樣就不用關心當前運行的程序存儲在什么地方了。這稱為絕對文件路徑 。在相對路徑行不通時,可使用絕對路徑。例如, 如果text_files并不在文件夾python_work中,而在文件夾other_files中,則向open() 傳遞路徑'text_files/ filename.txt' 行不通,因為Python只在文件夾python_work中查找 該位置。為明確地指出你希望Python到哪里去查找,你需要提供完整的路徑。
絕對路徑通常比相對路徑更長,因此將其存儲在一個變量中,再將該變量傳遞給open() 會有所幫助。在Linux和OS X中,絕對路徑類似于下面這樣:
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
而在Windows系統中,它們類似于下面這樣:
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
9.1.3 包含一百萬位的大型文件
前面我們分析的都是一個只有三行的文本文件,但這些代碼示例也可處理大得多的文件。如果我們有一個文本文件,其中包含精確到小數點后1 000 000位而不是30位的圓周率 值,也可創建一個包含所有這些數字的字符串。為此,我們無需對前面的程序做任何修改,只需將這個文件傳遞給它即可。在這里,我們只打印到小數點后50位,以免終端為顯 示全部1 000 000位而不斷地翻滾:
filename = 'pi_million_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ' '
for line in lines:
pi_string += line.strip()
print(pi_string[:52] + "...")
print(len(pi_string))
輸出表明,我們創建的字符串確實包含精確到小數點后1 000 000位的圓周率值:
3.14159265358979323846264338327950288419716939937510...
1000002
9.2 寫入文件
保存數據的最簡單的方式之一是將其寫入到文件中。通過將輸出寫入文件,即便關閉包含程序輸出的終端窗口,這些輸出也依然存在:你可以在程序結束運行后查看這些輸出,可與別人分享輸出文件,還可編寫程序來將這些輸出讀取到內存中并進行處理。
9.2.1 寫入空文件
write_message.py
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
programming.txt
I love programming.
9.2.2 附加到文件
如果你要給文件添加內容,而不是覆蓋原有的內容,可以附加模式 打開文件。你以附加模式打開文件時,Python不會在返回文件對象前清空文件,而你寫入到文件的行都將添加到文件末尾。如果指定的文件不存在,Python將為你創建一個空文件。 下面來修改write_message.py,在既有文件programming.txt中再添加一些你酷愛編程的原因:
write_message.py
filename = 'programming.txt'
with open(filename, 'a') as file_object:
file_object.write("I also love finding meaning in large datasets.\n")
file_object.write("I love creating apps that can run in a browser.\n")
programming.txt
I love programming.
I also love finding meaning in large datasets.
I love creating apps that can run in a browser.
9.3 異常
Python使用被稱為異常 的特殊對象來管理程序執行期間發生的錯誤。每當發生讓Python不知所措的錯誤時,它都會創建一個異常對象。如果你編寫了處理該異常的代碼,程序將繼 續運行;如果你未對異常進行處理,程序將停止,并顯示一個traceback,其中包含有關異常的報告。
異常是使用try-except 代碼塊處理的。try-except 代碼塊讓Python執行指定的操作,同時告訴Python發生異常時怎么辦。使用了try-except 代碼塊時,即便出現異常, 程序也將繼續運行:顯示你編寫的友好的錯誤消息,而不是令用戶迷惑的traceback。
9.3.1 處理ZeroDivisionError 異常
下面來看一種導致Python引發異常的簡單錯誤。你可能知道不能將一個數字除以0,但我們還是讓Python這樣做吧:
division.py
print(5/0)
顯然,Python無法這樣做,因此你將看到一個traceback:
Traceback (most recent call last):
File "division.py", line 1, in <module>
print(5/0)
ZeroDivisionError: division by zero
9.3.2 使用try-except代碼塊
當你認為可能發生了錯誤時,可編寫一個try-except 代碼塊來處理可能引發的異常。你讓Python嘗試運行一些代碼,并告訴它如果這些代碼引發了指定的異常,該怎么辦。 處理ZeroDivisionError 異常的try-except 代碼塊類似于下面這樣:
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
輸出結果:
You can't divide by zero!
9.3.3 使用異常避免崩潰
發生錯誤時,如果程序還有工作沒有完成,妥善地處理錯誤就尤其重要。這種情況經常會出現在要求用戶提供輸入的程序中;如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入,而不至于崩潰。
下面來創建一個只執行除法運算的簡單計算器:
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
if second_number == 'q':
break
answer = int(first_number) / int(second_number)
print(answer)
輸出結果:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
Traceback (most recent call last):
File "division.py", line 9, in <module>
answer = int(first_number) / int(second_number)
ZeroDivisionError: division by zero
9.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 = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
try:
answer = int(first_number) / int(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
9.3.5 處理FileNotFoundError異常
使用文件時,一種常見的問題是找不到文件:你要查找的文件可能在其他地方、文件名可能不正確或者這個文件根本就不存在。對于所有這些情形,都可使用try-except 代碼塊以直觀的方式進行處理。
我們來嘗試讀取一個不存在的文件。下面的程序嘗試讀取文件alice.txt的內容,但我沒有將這個文件存儲在alice.py所在的目錄中:
alice.py
filename = 'alice.txt'
with open(filename) as f_obj: contents = f_obj.read()
Python無法讀取不存在的文件,因此它引發一個異常:
Traceback (most recent call last):
File "alice.py", line 3, in <module>
with open(filename) as f_obj:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
在上述traceback中,最后一行報告了FileNotFoundError 異常,這是Python找不到要打開的文件時創建的異常。在這個示例中,這個錯誤是函數open() 導致的,因此要處理 這個錯誤,必須將try 語句放在包含open() 的代碼行之前:
filename = 'alice.txt'
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry, the file " + filename + " does not exist."
print(msg)
在這個示例中,try 代碼塊引發FileNotFoundError 異常,因此Python找出與該錯誤匹配的except 代碼塊,并運行其中的代碼。最終的結果是顯示一條友好的錯誤消息,而 不是traceback:
Sorry, the file alice.txt does not exist.