学习笔记 | 2021-7-24

第9章 类

面向对象编程是最有效的软件编写方法之一,你定义一大类对象都有的通用行为。根据类来创建对象被称为实例化。你还将编写类来扩展既有类的功能,让相似的类能够高效地共享代码。

9.1 创建和使用类

使用类几乎可以模拟任何东西。

9.1.1 创建Dog类

dog.py
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!")
  1. 方法__init__()
    类中的函数称为方法。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。在这个方法的定义中,形参self必不可少,还必须位于其他形参的前面。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。self会自动传递,因此我们不需要传递它。每当我们根据Dog类创建实例时,都只需要给最后两个形参(name和age)提供值。

  2. 在Python 2.7中创建类

 class ClassName(object):
 	-- snip --

9.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.“)

就算我们给第二条小狗指定同样的名字和年龄,Python依然会根据Dog类创建另一个实例。你可按需求根据一个类创建任意数量的实例,条件是将每个实例都存储在不同的变量中,或占用列表或字典的不同位置。

9.2 使用类和实例

9.2.1 Car类

car.py
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())

9.2.2 给属性指定默认值

类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。

9.2.3 修改属性的值

可以以三种不同的方式修改属性的值,直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值)。
1.直接修改属性的值

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.odometer_reading = 23
my_new_car.read_odometer()

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 update_odometer(self, mileage):
		self.odometer_reading = mileage
		
	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.odometer_reading = 23
my_new_car.read_odometer()

3. 通过方法对属性的值进行递增

9.3 继承

原有的类称为父类,而新类称为子类。

9.3.1 子类的方法__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())

super()是一个特殊函数,帮助Python将父类和子类关联起来。父类也称为超类,名称super因此而得名。

9.3.2 Python 2.7中的继承

9.3.3 给子类定义属性和方法

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

对于ElectricCar类的特殊化程度没有任何限制。模拟电动汽车时,你可以根据所需的准确程度添加任意数量的属性和方法。

9.3.4 重写父类的方法

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

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

def fill_gas_tank(self):
	print("This car doesn't need a gas tank!")

使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。

9.3.5 将实例用作属性

使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。

例如,不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作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 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()

这看似做了很多额外的工作,但现在我们想多详细地描述电瓶都可以,且不会导致ElectricCar类混乱不堪。下面再给Battery类添加一个方法,它根据电瓶容量报告汽车的续航里程:

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 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.")
		
	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_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

9.3.6 模拟实物

这让你进入了程序员的另一个境界:解决上述问题时,你从较高的逻辑层面(而不是语法层面)考虑;你考虑的不是Python,而是如何使用代码来表示实物。到达这种境界后,你经常会发现,现实世界的建模方法并没有对错之分。有些方法的效率更高,但要找到效率最高的表示法,需要经过一定的实践。只要代码像你希望的那样运行,就说明你做的很好!即使你发现自己不得不多次尝试使用不同的方法来重写类,也不必气馁;要编写出高效、准确的代码,都得经过这样的过程。

9.4 导入类

随着你不断地给类添加功能,文件可能变得很长,即便你妥善地使用了继承亦如此。为遵循Python的总体理念,应让文件尽可能简洁。为在这方面提供帮助,Python允许你将类存储在模块中,然后在主程序中导入所需的模块。

9.4.1 导入单个类

car.py
--snip--

my_car.py
from car import Car

导入类是一种有效的编程方式,可以专注于主程序的高级逻辑。

9.4.2 在一个模块中存储多个类

虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类。

car.py
class Car():

class Battery():

class ElectricCar(Car):
my_electric_car.py
from car import ElectricCar

9.4.3 从一个模块中导入多个类

my_cars.py
from car import Car, ElectricCar

9.4.4 导入整个模块

my_cars.py
import car

9.4.5 导入模块中的所有类

from module_name import *

不推荐使用这种导入方式,如果你不小心导入了一个与程序文件中其他东西同名的类,将引发难以判断的错误。
需要从一个模块中导入很多类时,最好导入整个模块,并使用module_name.class_name语法来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地方使用了导入的模块;你还避免了导入模块中的每个类可能引发的名称冲突。

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

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

9.5 Python标准库

Python标准库是一组模块,安装的Python都包含它。

favorite_languages.py
from collections import OrderedDict

favorite_languages = OrderedDict()

favorite_languages['jen'] = 'python'
favorite_languages['sarah'] = 'c'
favorite_languages['edward'] = 'ruby'
favorite_languages['phil'] = 'python'

for name, language in favorite_languages.items():
	print(name.title() + "'s favorite language is " + language.title() + ".")

随着你对标准库的了解越来越深入,将熟悉大量可帮助你处理常见情形的模块。
你还可以从其他地方下载外部模块。本书第二部分的每个项目都需要使用外部模块,届时你将看到很多这样的案例。

9.6 类编码风格

类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不适用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。
对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都包含一个文档字符串,对其中的类可用于做什么进行描述。
可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值