跟上時代的步伐,學一波Python(二)

相關閱讀:跟上時代的步伐,學一波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.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373