1类
1.1类和实例
1 class Dog(): 2 """一次模拟小狗的简单尝试""" 3 def __init__(self, name, age): 4 """初始化属性name和age""" 5 self.name = name 6 self.age = age 7 def sit(self): 8 """模拟小狗被命令时蹲下""" 9 print(self.name.title() + " is now sitting.") 10 def roll_over(self): 11 """模拟小狗被命令时打滚""" 12 print(self.name.title() + " rolled over!") 13 my_dog = Dog('willie', 6) 14 your_dog = Dog('lucy', 3) 15 print("My dog's name is " + my_dog.name.title() + ".") 16 print("My dog is " + str(my_dog.age) + " years old.") 17 my_dog.sit() 18 print(" Your dog's name is " + your_dog.name.title() + ".") 19 print("Your dog is " + str(your_dog.age) + " years old.") 20 your_dog.sit()
类中的函数称为方法;你前面学到的有关函数的一切都适用于方法,就目前而言,唯一重要的差别是调用方法的方式。方法__init__() 是一个特殊的方法,每当你根据Dog 类创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。我们将方法__init__() 定义成了包含三个形参:self 、name 和age 。在这个方法的定义中,形参self 必不可少,还必须位于其他形参的前面。为何必须在方法定义中包含形参self 呢?因为Python调用这个__init__() 方法来创建Dog 实例时,将自动传入实参self 。每个与类相关联的方法调用都自动传递实参self ,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。我们创建Dog 实例时,Python将调用Dog 类的方法__init__() 。我们将通过实参向Dog() 传递名字和年龄;self 会自动传递,因此我们不需要传递它。每当我们根据Dog 类创建实例时,都只需给最后两个形参(name 和age )提供值。
1.2给属性指定默认的值
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("这辆车已跑的里程数为:"+str(self.odometer_reading)) my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.read_odometer()
1.3修改属性的值
可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值)。下面依次介绍这些方法。
1.3.1直接修改属性的值
my_new_car.odometer_reading=23
1.3.2通过方法修改属性的值
def update_odometer(self,mileage): self.odometer_reading=mileage my_new_car.update_odometer(36)#调用方法
1.3.3 通过方法对属性的值进行递增通过方法对属性的值进行递增
def increment_odometer(self, miles): """将里程表读数增加指定的量""" self.odometer_reading += miles
注意 你可以使用类似于上面的方法来控制用户修改属性值(如里程表读数)的方式,但能够访问程序的人都可以通过直接访问属性来将里程表修改为任何值。要确保安全,除了进行类似于前面的基本检查外,还需特别注意细节。
1.4继承
class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year)
super() 是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar 的父类的方法__init__() ,让ElectricCar 实例包含父类的所有属性。父类也称为超类 (superclass),名称super因此而得名。
- 给子类定义属性和方法
class ElectricCar(Car): 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.")
- 重写父类的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。假设Car 类有一个名为fill_gas_tank() 的方法,它对全电动汽车来说毫无意义,因此你可能想重写它。下面演示了一种重写方式:
def fill_gas_tank(self): """电动汽车没有油箱""" print("This car doesn't need a gas tank!")
现在,如果有人对电动汽车调用方法fill_gas_tank() ,Python将忽略Car 类中的方法fill_gas_tank() ,转而运行上述代码。使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。
- 将实例用作属性
使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。例如,不断给ElectricCar 类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery 的类中,并将一个Battery 实例用作ElectricCar 类的一个属性:
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.")
"""此时的inti方法写法如下"""
def __init__(self,make,model,year): super().__init__(make,model,year) self.battery=Battery() my_tesla.battery.describe_battery()#实例调用
1.5导入类
- 导入单个类
from 模块名 import 类名
- 从一个模块中导入多个类
from 模块名 import 类名1,类名2
- 导入整个模块
import 模块名
- 导入模块中的所有类
from 模块名 import *
注意:不推荐使用这种导入方式,其原因有二。首先,如果只要看一下文件开头的import 语句,就能清楚地知道程序使用了哪些类,将大有裨益;但这种导入方式没有明确地指出你使用了模块中的哪些类。这种导入方式还可能引发名称方面的困惑。如果你不小心导入了一个与程序文件中其他东西同名的类,将引发难以诊断的错误。这里之所以介绍这种导入方式,是因为虽然不推荐使用这种方式,但你可能会在别人编写的代码中见到它。需要从一个模块中导入很多类时,最好导入整个模块,并使用 module_name.class_name 语法来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地方使用了导入的模块;你还避免了导入模块中的每个类可能引发的名称冲突。
2文件和异常
with open('pi_digits.txt') as read_object: contents=read_object.read() print(contents.rstrip())
函数open()接受一个参数:要打开的文件的名称。Python在当前执行的文件所在的目录中查找指定的文件。函数open() 返回一个表示文件的对象。在这里,open('pi_digits.txt') 返回一个表示文件pi_digits.txt 的对象;Python将这个对象存储在我们将在后面使用的变量中。
关键字with 在不再需要访问文件后将其关闭。在这个程序中,注意到我们调用了open() ,但没有调用close() ;你也可以调用open() 和close() 来打开和关闭文件,但这样做时,如果程序存在bug,导致close() 语句未执行,文件将不会关闭。这看似微不足道,但未妥善地关闭文件可能会导致数据丢失或受损。如果在程序中过早地调用close() ,你会发现需要使用文件时它已关闭关闭 (无法访问),这会导致更多的错误。并非在任何情况下都能轻松确定关闭文件的恰当时机,但通过使用前面所示的结构,可让Python去确定:你只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭。
2.2创建一个包含文件各行内容的列表
使用关键字with 时,open() 返回的文件对象只在with 代码块内可用。如果要在with 代码块外访问文件的内容,可在with 代码块内将文件的各行存储在一个列表中,并在with 代码块外使用该列表:你可以立即处理文件的各个部分,也可推迟到程序后面再处理。下面的示例在with 代码块中将文件pi_digits.txt的各行存储在一个列表中,再在with 代码块外打印它们:
1 filename = 'pi_digits.txt' 2 with open(filename) as file_object: 3 lines = file_object.readlines() 4 for line in lines: 5 print(line.rstrip())
2.3写入文件
1 filename = 'programming.txt' 2 with open(filename, 'w') as file_object: 3 file_object.write("I love programming.")
在这个示例中,调用open() 时提供了两个实参。第一个实参也是要打开的文件的名称;第二个实参('w' )告诉Python,我们要以写入模式写入模式 打开这个文件。打开文件时,可指定读取模式读取模式 ('r' )、写入模式写入模式 ('w' )、附加模式附加模式 ('a' )或让你能够读取和写入文件的模式('r+' )。如果你省略了模式实参,Python将以默认的只读模式打开文件。如果你要写入的文件不存在,函数open() 将自动创建它。然而,以写入('w' )模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
2.4异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。异常是使用try-except 代码块处理的。try-except 代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用了try-except 代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
语法结构:try-except-else代码块的工作原理大致如下:Python尝试执行try 代码块中的代码;只有可能引发异常的代码才需要放在try 语句中。有时候,有一些仅在try 代码块成功执行时才需要运行的代码;这些代码应放在else 代码块中。except 代码块告诉Python,如果它尝试运行try 代码块中的代码时引发了指定的异常,该怎么办。