6. 字典
6.1 一个简单的字典
字典 alien_0 存储了外星人的颜色和点数.使用两条 print 语句来访问并打印这些信息,如下所示:
alien_0 = {'color': 'green', 'points': 5}
print(alien_0['color'])
print(alien_0['points'])
5
green
6.2 使用字典在 Python 中,字典 是一系列键—值对 .每个键 都与一个值相关联,你可以使用键来访问与之相关联的值.与键相关联的值可以是数字,字符串,列表乃至字典.事实上,可将任何 Python 对象用作字典中的值.
在 Python 中,字典用放在花括号 {} 中的一系列键—值对表示,如前面的示例所示:
alien_0 = {'color': 'green', 'points': 5}
6.2.1 访问字典中的值
要获取与键相关联的值,可依次指定字典名和放在方括号内的键,如下所示:
这将返回字典 alien_0 中与键'color' 相关联的值:
alien_0 = {
'color': 'green'print(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)
6.2.3 修改字典中的值
{'color': 'green', 'points': 5}
{'color': 'green', 'points': 5, 'y_position': 25, 'x_position': 0}
要修改字典中的值,可依次指定字典名,用方括号括起的键以及与该键相关联的新值.
输出结果:
alien_0 = {
'color': 'green'
}
print("The alien is " + alien_0['color'] + ".") alien_0['color'] = 'yellow'print("The alien is now " + alien_0['color'] + ".")
6.2.4 删除键—值对
The alien is green.
The alien is now yellow.
对于字典中不再需要的信息,可使用 del 语句将相应的键—值对彻底删除.使用 del 语句时,必须指定字典名和要删除的键.
输出结果:
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
del alien_0['points']
print(alien_0)
6.3 遍历字典
{'color': 'green', 'points': 5}
{'color': 'green'
一个 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)
6.3.2 遍历字典中所有键
Key: last
Value: fermi
Key: first
Value: enrico
Key: username
Value: efermi
在不需要使用字典中的值时,方法 keys() 很有用.下面来遍历字典 favorite_languages ,并将每个被调查者的名字都打印出来:
输出结果:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
for name in favorite_languages.keys() : print(name.title())
6.3.3 按顺序遍历字典中的所有键
Jen
Sarah
Phil
Edward
字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的.这不是问题,因为通常你想要的只是获取与键相关联的正确的值. 要以特定的顺序返回元素,一种办法是在 for 循环中对返回的键进行排序.为此,可使用函数 sorted() 来获得按特定顺序排列的键列表的副本:
输出结果:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
for name in sorted(favorite_languages.keys()) : print(name.title())
6.3.4 遍历字典中的所有值
Edward
Jen
Phil
Sarah
这条 for 语句提取字典中的每个值,并将它们依次存储到变量 language 中.通过打印这些值,就获得了一个列表,其中包含被调查者选择的各种语言:
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())
7. 函数
The following languages have been mentioned:
Python
C
Python
Ruby
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() 时,可将一个名字 传递给它,如下所示:
代码 greet_user('jesse') 调用函数 greet_user() ,并向它提供执行 print 语句所需的信息.这个函数接受你传递给它的名字,并向这个人发出问候:
def greet_user(username):
"""显示简单的问候语"""
print("Hello, " + username.title() + "!")
greet_user('jesse')
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')
7.2.2 关键字实参
I have a harry.
My harry's name is Hamster.
关键字实参 是传递给函数的名称—值对.你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆 (不会得到名为 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')
7.2.3 默认值
I have a harry.
My harry's name is Hamster.
编写函数时,可给每个形参指定默认值 .在调用函数中给形参提供了实参时,Python 将使用指定的实参值; 否则,将使用形参的默认值.因此,给形参指定默认值后,可在函数调用中省略相应的实参.使用默认值可简化函数调用,还可清楚地指出函数的典型用法.
这里修改了函数 describe_pet() 的定义,在其中给形参 animal_type 指定了默认值'dog'.这样,调用这个函数时,如果没有给 animal_type 指定值,Python 将把这个 形参设置为'dog' :
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')
7.3 返回值
I have a dog.
My dog's name is Willie.
函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值.函数返回的值被称为返回值 .在函数中,可使用 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 循环,以更正规的方式问候用户.下面尝试使用名
我们添加了一条消息来告诉用户如何退出,然后在每次提示用户输入时,都检查他输入的是否是退出值,如果是,就退出循环.现在,这个程序将不断地问候,直到用户输入的姓或名为'q' 为止:
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 + "!")
7.4 传递列表
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
输出结果:
def greet_users(names):
"""向列表中的每位用户都发出简单的问候"""
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
7.5 将函数存在模块中
Hello, Hannah!
Hello, Ty!
Hello, Margot!
函数的优点之一是,使用它们可将代码块与主程序分离.通过给函数指定描述性名称,可让主程序容易理解得多.你还可以更进一步,将函数存储在被称为模块 的独立文件中,再将模块导入 到主程序中.import 语句允许在当前运行的程序文件中使用模块中的代码. 通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上.这还能让你在众多不同的程序中重用函数.将函数存储在独立文件中后,可与其他程序员共享这些文件而不是整个程序.知道如何导入函数还能让你使用其他程序员编写的函数库.
7.5.1 导入整个模块
要让函数是可导入的,得先创建模块.模块 是扩展名为. py 的文件,包含要导入到程序中的代码.下面来创建一个包含函数 make_pizza() 的模块.为此,我们将文件 pizza.py 中除函数 make_pizza() 之外的其他代码都删除:
接下来,我们在 pizza.py 所在的目录中创建另一个名为 making_pizzas.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)
Python 读取这个文件时,代码行 import pizza 让 Python 打开文件 pizza.py,并将其中的所有函数都复制到这个程序中.你看不到复制的代码,因为这个程序运行时,Python 在幕 后复制这些代码.你只需知道,在 making_pizzas.py 中,可以使用 pizza.py 中定义的所有函数.
making_pizzas.py
import pizza
pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
7.6.2 导入特定的函数
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
你还可以导入模块中的特定函数,这种导入方法的语法如下:
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 将函数重命名为你提供的别名:
上面的 import 语句将函数 make_pizza() 重命名为 mp() ; 在这个程序中,每当需要调用 make_pizza() 时,都可简写成 mp() ,而 Python 将运行 make_pizza() 中的代 码,这可避免与这个程序可能包含的函数 make_pizza() 混淆.
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
指定别名的通用语法如下:
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 导入模块中的所有函数:
8. 类
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
8.1 创建和使用类
使用类几乎可以模拟任何东西.下面来编写一个表示小狗的简单类 Dog ——它表示的不是特定的小狗,而是任何小狗.对于大多数宠物狗,我们都知道些什么呢? 它们都有名字 和年龄; 我们还知道,大多数小狗还会蹲下和打滚.由于大多数小狗都具备上述两项信息 (名字和年龄) 和两种行为(蹲下和打滚),我们的 Dog 类将包含它们.这个类让 Python 知道如何创建表示小狗的对象.编写这个类后,我们将使用它来创建表示特定小狗的实例.
8.1.1 创建 Dog 类
8.1.2 根据类创建实例
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!")
输出结果
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()
8.2 使用类和实例
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.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()
8.2.3 修改属性的值
2016 Audi A4
This car has 0 miles on it.
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()
2. 通过方法修改属性的值
2016 Audi A4
This car has 23 miles on it.
输出结果:
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()
3. 通过方法对属性的值进行递增
2016 Audi A4
This car has 23 miles on it.
有时候需要将属性值递增特定的量,而不是将其设置为全新的值.假设我们购买了一辆二手车,且从购买到登记期间增加了 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()
8.3 继承
2013 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.
编写类时,并非总是要从空白开始.如果你要编写的类是另一个现成类的特殊版本,可使用继承 .一个类继承 另一个类时,它将自动获得另一个类的所有属性和方法; 原有的类称为父类 ,而新类称为子类 .子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法.
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 类的定义类似于下面这样:
8.3.3 给子类定义属性和方法
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--
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法. 下面来添加一个电动汽车特有的属性 (电瓶),以及一个描述该属性的方法.我们将存储电瓶容量,并编写一个打印电瓶描述的方法:
输出结果:
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()
8.3.4 重写父类的方法
2016 Tesla Model S
This car has a 70-kWh battery.
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写.为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名.这样,Python 将不会考虑这个父类方法,而只关注你在子类中定义的相应方法.
8.3.5 将实例用作属性
def ElectricCar(Car):
--snip--
def fill_gas_tank():
"""电动汽车没有油箱"""
print("This car doesn't need a gas tank!")
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多: 属性和方法清单以及文件都越来越长.在这种情况下,可能需要将类的一部分作为一个独立的类提取出来.你可以将大型类拆分成多个协同工作的小类.
例如,不断给 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()
8.4 导入类
my_tesla.battery.describe_battery()
2016 Tesla Model S
This car has a 70-kWh battery
随着你不断地给类添加功能,文件可能变得很长,即便你妥善地使用了继承亦如此.为遵循 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):
"""
拒绝将里程表往回拨
下面来创建另一个文件——my_car.py,在其中导入 Car 类并创建其实例:
"""
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'
输出结果:
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()
8.4.2 在一个模块中存储多个类
2016 Audi A4
This car has 23 miles on it.
虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类.类 Battery 和 ElectricCar 都可帮助模拟汽车,因此下面将它们都加入模块 car.py 中:
现在,可以新建一个名为 my_electric_car.py 的文件,导入 ElectricCar 类,并创建一辆电动汽车了:
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
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()
8.4.3 从一个模块中导入多个类
2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
输出结果:
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())
8.4.4 导入整个模块
2016 Volkswagen Beetle
2016 Tesla Roadster
你还可以导入整个模块,再使用句点表示法访问需要的类.这种导入方法很简单,代码也易于阅读.由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突.
9. 文件和异常
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.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)
相比于原始文件,该输出唯一不同的地方是末尾多了一个空行.为何会多出这个空行呢? 因为 read() 到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一 个空行.要删除多出来的空行,可在 print 语句中使用 rstrip() :
3.1415926535
8979323846
2643383279
前面说过,Python 方法 rstrip() 删除 (剥除) 字符串末尾的空白.现在,输出与原始文件的内容完全相同:
with open('pi_digits.txt') as file_object: contents = file_object.read()
print(contents.rstrip())
9.1.2 文件路径
3.1415926535
8979323846
2643383279
当你将类似 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 中,绝对路径类似于下面这样:
而在 Windows 系统中,它们类似于下面这样:
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
9.1.3 包含一百万位的大型文件
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
前面我们分析的都是一个只有三行的文本文件,但这些代码示例也可处理大得多的文件.如果我们有一个文本文件,其中包含精确到小数点后 1 000 000 位而不是 30 位的圆周率 值,也可创建一个包含所有这些数字的字符串.为此,我们无需对前面的程序做任何修改,只需将这个文件传递给它即可.在这里,我们只打印到小数点后 50 位,以免终端为显 示全部 1 000 000 位而不断地翻滚:
输出表明,我们创建的字符串确实包含精确到小数点后 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))
9.2 写入文件
3.14159265358979323846264338327950288419716939937510...
1000002
保存数据的最简单的方式之一是将其写入到文件中.通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在: 你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理.
9.2.1 写入空文件
9.2.2 附加到文件
write_message.py
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
programming.txt
I love programming.
如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式 打开文件.你以附加模式打开文件时,Python 不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾.如果指定的文件不存在,Python 将为你创建一个空文件. 下面来修改 write_message.py,在既有文件 programming.txt 中再添加一些你酷爱编程的原因:
9.3 异常
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.
Python 使用被称为异常 的特殊对象来管理程序执行期间发生的错误.每当发生让 Python 不知所措的错误时,它都会创建一个异常对象.如果你编写了处理该异常的代码,程序将继 续运行; 如果你未对异常进行处理,程序将停止,并显示一个 traceback,其中包含有关异常的报告.
异常是使用 try-except 代码块处理的.try-except 代码块让 Python 执行指定的操作,同时告诉 Python 发生异常时怎么办.使用了 try-except 代码块时,即便出现异常, 程序也将继续运行: 显示你编写的友好的错误消息,而不是令用户迷惑的 traceback.
9.3.1 处理 ZeroDivisionError 异常
下面来看一种导致 Python 引发异常的简单错误.你可能知道不能将一个数字除以 0,但我们还是让 Python 这样做吧:
显然,Python 无法这样做,因此你将看到一个 traceback:
division.py
print(5/0)
9.3.2 使用 try-except 代码块
Traceback (most recent call last):
File "division.py", line 1, in <module>
print(5/0)
ZeroDivisionError: division by zero
当你认为可能发生了错误时,可编写一个 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)
9.3.4 else 代码块
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
通过将可能引发错误的代码放在 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)
9.3.5 处理 FileNotFoundError 异常
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 代码块以直观的方式进行处理.
我们来尝试读取一个不存在的文件.下面的程序尝试读取文件 alice.txt 的内容,但我没有将这个文件存储在 alice.py 所在的目录中:
Python 无法读取不存在的文件,因此它引发一个异常:
alice.py
filename = 'alice.txt'
with open(filename) as f_obj: contents = f_obj.read()
在上述 traceback 中,最后一行报告了 FileNotFoundError 异常,这是 Python 找不到要打开的文件时创建的异常.在这个示例中,这个错误是函数 open() 导致的,因此要处理 这个错误,必须将 try 语句放在包含 open() 的代码行之前:
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'
在这个示例中,try 代码块引发 FileNotFoundError 异常,因此 Python 找出与该错误匹配的 except 代码块,并运行其中的代码.最终的结果是显示一条友好的错误消息,而 不是 traceback:
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)
Sorry, the file alice.txt does not exist.
来源: http://www.jianshu.com/p/dd970129e506