跟上时代的步伐,学一波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阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345