1. 创建和使用类 1.1 创建Dog类 下面将创建一个表示小狗的Dog类,根据Dog类创建的每个示例都将存储名字和年龄,并且赋予每条小狗蹲下(sit())和打滚(roll_over())的能力:
dog.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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!" )
在上面的代码中,定义了一个名为Dog的类。根据约定,在Python中,首字母大写的名称指的是类 。这个类定义中的括号是空的,说明该类并没有需要继承的父类。
1.1.1 方法__init__() 类中的函数称为方法,而__init__()是一个特殊的方法,每当你根据Dog类创建新示例时,Python都会自动运行它,类似于C++类中的构造函数,在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
我们将方法__init__()定义成了包含三个实参:self、name和age。在这个方法的定义中,形参self是必不可少的,还必须位于其他形参的前面,每个与类相关联的方法调用都自动传递实参self,它是一个指向示例本身的引用,让实例能够访问类中的属性和方法 。我们将通过实参向Dog()传递名字和年龄,self会自动传递,因此我们不需要传递它,只需要给最后两个形参(name和age)提供值。
在类中,以self为前缀的变量都可供类中的所有方法使用,我们还可以通过类的实例来访问这些变量。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。self.age = age的作用相同。像这样在类中可通过实例访问的变量称为属性 。
1.2 根据类创建实例 可将类视为有关如何创建实例的说明,Dog类是一系列说明,让Python知道如何创建表示特定小狗的实例。下面来创建一个表示特定小狗的实例:
1 2 3 4 5 6 7 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." )
在上面这段代码中,创建了一个名字为’willie’、年龄为6的小狗。遇到这行代码时,Python将实参'willie'和6传入Dog类中的方法__init__()。方法__init__()创建一个便是特定小狗的实例,并使用实参的值来设置属性name和age。方法__init__()并未显式地包含return语句,但Python自动返回一个表示这条小狗的实例,并将这个实例存储在变量my_dog中。在Python中,我们通常可以认为首字母大写的名称(如Dog)指的是类,而小写的名称(如my_dog)指的是根据类创建的实例。
1.2.1 访问属性 要访问实例的属性,可使用句点表示法:
在这里,Python先找到实例my_dog,再查找与这个实例相关联的属性name。在Dog类中引用这个属性时,使用的是self.name,因此,在上面的实例化代码中,打印出的结果如下所示:
1 2 My dog's name is Willie. My dog is 6 years old.
1.2.2 调用方法 根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法:
1 2 3 4 5 6 class Dog (): --snip-- my_dog = Dog('willie' , 6 ) my_dog.sit() my_dog.roll_over()
要调用方法,可指定实例的名称和要调用的方法,并用句点分隔它们。上面的代码中运行的输出结果如下所示:
1 2 Willie is now sitting. Willie rolled over!
2. 使用类和实例 在类编写好后,你的大部分时间都将花在使用根据类创建的实例上,你需要执行的一个重要任务是修改实例的属性。你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。
2.1 Car类 下面是一个表示汽车的类,它存储了有关汽车的信息,还有一个汇总这些信息的方法:
car.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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())
在上面的代码中,输出结果如下所示:
2.2 给属性指定默认值 类中的每个属性都必须有初始值,哪怕这个值是0或是空字符。在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的,如如果你对某个属性这样做了,就无需包含为它提供初始值的形参。
下面来添加一个名为odometer_reading的属性,其初始值总是为0。除此之外,还添加了一个名为read_odometer()的方法,用于读取汽车的里程表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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." ) my_new_car = Car('audi' , 'a4' , 2016 ) print (my_new_car.get_descriptive_name())my_new_car.read_odometer()
上面这段代码打印的结果如下,由于未对汽车里程进行修改,因此汽车的里程还是0:
1 2 2016 Audi A4This car has 0 miles on it.
2.3 修改属性的值 在Python中,有如下两种不同的方式可以修改属性的值:直接通过实例进行修改;通过方法进行设置。下面依次介绍这些方法。
2.3.1 直接修改属性的值 要修改属性的值,最简单的方式是通过实例直接访问它。下面的代码直接将里程表度数设置为23:
1 2 3 4 5 6 7 8 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()
此时,my_new_car这个实例中的odometer_reading属性就被修改为23:
1 2 2016 Audi A4This car has 23 miles on it.
2.3.2 通过方法修改属性的值 下面的例子演示了一个名为update_odometer()的方法:
1 2 3 4 5 6 7 8 9 10 11 12 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()
上面的代码中添加了一个新的方法update_odometer()。这个方法接受一个里程值,并将其存储到self.odometer_reading中。上面的代码打印结果如下:
1 2 2016 Audi A4This car has 23 miles on it.
3. 继承 编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法 。原有的类称为父类,而新类称为它的子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
3.1 子类的方法__init__() 创建子类时的示例时,Python首先需要先给父类的所有属性赋值,为此,子类的方法__init__()需要依靠父类。
下面的代码创建一个简单的ElectricCar类,它具备Car类的所有功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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!" ) 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())
创建子类时,父类必须包含在当前文件中,且位于子类前面,在上面的代码中,我们定义了子类ElectricCar。定义子类时,必须在括号内指定父类的名称。方法__init__()接受创建Car示例所需的信息。super()是一个特殊函数,帮助Python将父类和子类关联起来,这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超类,名称super()因此而得名。上面这段代码的输出结果为:
可以看到继承自Car类的ElectricCar类确实具有和其父类Car相同的功能。
3.2 给子类定义属性和方法 让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法,下面的代码添加一个电动车特有的属性(电瓶),以及一个描述该属性的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Car (): --snip-- 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." ) my_tesla = ElectricCar('tesla' , 'model s' , 2016 ) print (my_tesla.get_descriptive_name())my_tesla.describe_battery()
在上面这行代码中,添加了新属性self.battery_size,并设置初始值为70。根据ElectricCar类创建的所有实例都将包含这个属性,但所有Car实例都不包含它。除此之外,还添加了一个describe_battery()方法,它打印有关电瓶的信息。我们调用这个方法时,将看到一条电动车特有的描述:
1 2 2016 Tesla Model SThis car has a 70 -kWh battery.
3.3 重写父类方法 对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中重新定义的该方法。
假设Car类有一个名为fill_gas_tank()的方法,它对全电动车来说毫无意义,下面演示了一种重写方式:
1 2 3 4 5 6 class ElectricCar (Car ): --snip-- def fill_gas_tank (): """ 电动汽车没有邮箱 """ print ("This car doesn't need a gas tank!" )
现在,如果有人对电动车调用方法fill_gas_tank(),Python将忽略Car类中的方法fill_gas_tank()。转而运行上述代码。使用继承时,可让子类保留从父类哪里继承而来的精华,并剔除不需要的糟粕。
3.4 将实例用作属性 在Python中,可以将大型类拆分成多个协同工作的小类,例如在下面的代码中,将针对汽车电瓶的属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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()
在上面的代码中,定义了一个名为Battery的新类,在ElectricCar这个类中,创建了一个名为self.battery的Battery实例。现在每个ElectricCar实例都包含一个自动创建的Battery实例。输出结果如下所示:
1 2 2016 Tesla Model SThis car has a 70 -kWh battery.