Python 类

类的创建和使用

创建一个小狗类:

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!")

里面有三个方法,分别是__init__方法,sit方法和roll_over方法。
__init__() 是一个特殊的方法,每当你根据 Dog 类创建新实例时, Python 都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免 Python 默认方法与普通方法发生名称冲突。
方法 __init__() 定义成了包含三个形参: self 、 name 和 age 。在这个方法的定义中,形参 self 必不可少,还必须位于其他形参的前面。为何必须在方法定义中包含形参 self 呢?因为 Python 调用这个 __init__() 方法来创建 Dog 实例时,将自动传入实参 self 。每个与类相关联的方法调用都自动传递实参 self ,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。

sit() 和 roll_over() 。由于这些方法不需要额外的信息,如名字或年龄,因此它们只有一个形参 self 。

可根据类创建和使用实例

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.sit()
my_dog.roll_over()

可以看到,python通过句点访问属性和方法。

类的继承与重写

编写一个Car类及其子类 ElectricCar:

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())

创建子类时,父类必须包含在当前文件中,且位于子类前面。定义子类时,必须在括号内指定父类的名称。方法 __init__() 接受创建 Car 实例所需的信息。

super() 是一个特殊函数,帮助 Python 将父类和子类关联起来。这行代码让 Python 调用 ElectricCar 的父类的方法 __init__() ,让 ElectricCar 实例包含父类的所有属性。父类也称为超类 ( superclass ),名称 super 因此而得名。

在 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--

函数 super() 需要两个实参:子类名和对象 self 。为帮助 Python 将父类和子类关联起来,这些实参必不可少。另外,在 Python 2.7 中使用继承时,务必在定义父类时在括号内指定 object 。

重写父类的方法

对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样, Python 将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。

假设 Car 类有一个名为 fill_gas_tank() 的方法,它对全电动汽车来说毫无意义,因此你可能想重写它。下面演示了一种重写方式:

def ElectricCar(Car):
	--snip--
def fill_gas_tank():
	""" 电动汽车没有油箱 """
	print("This car doesn't need a gas tank!")

现在,如果有人对电动汽车调用方法 fill_gas_tank() , Python 将忽略 Car 类中的方法 fill_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()

要描述电瓶时,需要使用电动汽车的属性 battery :

my_tesla.battery.describe_battery()

这行代码让 Python 在实例 my_tesla 中查找属性 battery ,并对存储在该属性中的Battery 实例调用方法 describe_battery() 。

导入类
导入单个类

下面来创建一个只包含 Car 类的模块,文件名为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()
导入多个类

在一个模块中存储多个类
虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类。类 Battery 和 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 的文件,导入 ElectricCar 类,并创建一辆电动汽车了:

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()

可根据需要在程序文件中导入任意数量的类。如果我们要在同一个程序中创建普通汽车和电动汽车,就需要将 Car 和 ElectricCar 类都导入:

文件名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())

也可以导入整个模块或模块中的所有类:
导入整个模块:

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())

导入模块中的所有类:

from car import *

对于 from module_name import *,不推荐使用这种导入方式,其原因有二。
首先,如果只要看一下文件开头的 import 语句,就能清楚地知道程序使用了哪些类,将大有裨益;其次这种导入方式不但没有明确地指出你使用了模块中的哪些类,还可能引发名称方面的困惑。如果你不小心导入了一个与程序文件中其他东西同名的类,将引发难以诊断的错误。这里之所以介绍这种导入方式,是因为虽然不推荐使用这种方式,但你可能会在别人编写的代码中见到它。

需要从一个模块中导入很多类时,最好导入整个模块,并使用module_name.class_name 语法来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地方使用了导入的模块,你还避免了导入模块中的每个类可能引发的名称冲突。

在一个模块中导入另一个模块

有时候,需要将类分散到多个模块中,以免模块太大,或在同一个模块中存储不相关的类。将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类。在这种情况下,可在前一个模块中导入必要的类。

例如,下面将 Car 类存储在一个模块中,并将 ElectricCar 和 Battery 类存储在另一个模块中。则在这种情况下,在写ElectricCar 和 Battery 类的代码前,需要导入car模块。

""" 一组可用于表示电动汽车的类 """
from car import Car

class Battery():
--snip--

class ElectricCar(Car):
--snip--

开发建议:在编写复杂的类代码时,一开始应让代码结构尽可能简单。先尽可能在一个文件中完成所有的工作,确定一切都能正确运行后,再将类移到独立的模块中。如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中。先找出让你能够编写出可行代码的方式,再尝试让代码更为组织有序。

类编码风格

你必须熟悉有些与类相关的编码风格问题,在你编写的程序较复杂时尤其如此。

类名应采用 驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。

对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应包含一个文档字符串,对其中的类可用于做什么进行描述。

可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。

需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的 import 语句,再添加一个空行,然后编写导入你自己编写的模块的 import 语句。在包含多
条 import 语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何方。

参考:
《python:从入门到实践》第8章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值