六、面向对象(OOP)
这一篇我们将介绍python有关面向对象的相关概念,做为初学者首先要理解什么是面向对象,它有什么用。带着这几个问题我们更清楚学习面向对象编程的意义。
面向对象编程是通过模拟现实世界中的对象来构建软件系统,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程。它通过封装、继承和多态等机制,将数据和功能结合在一起,形成对象,并通过消息传递来实现对象间的通信。
最早提出面向对象概念的大牛-阿伦·科提斯·凯伊(英语:Alan Curtis Kay),他出生于1940年5月17日,呵呵,他比川普还大6岁,这位大牛不光提出了面向对象的概念,还参与编写了第一个动态的面向对象编程语言Smalltalk,同时他也是计算机图灵奖获得者,要知道图灵奖可是计算机界的诺贝尔奖,苹果公司院士、惠普公司资深院士、加州大学兼职教授、爵士吉他手、作曲家和戏剧设计师、古典管风琴手,这些都是他的标签,毫无疑问这是一位天赋型选手。在开启面向对象之旅前,向这位伟大的前辈致敬。
面向对象编程思想可以帮助我们更好的把软件构建和现实生产生活中的事务联系起来,合理高效的解决问题,也避免我们重复造轮子。
1.基本概念
(1)类(class)
类是创建对象的蓝图或模板。类定义了对象的属性和方法。以特斯拉汽车为例,特斯拉汽车这个概念就是一个类。
class Tesla:
# 类属性,所有 Tesla 实例共享
manufacturer = "Tesla"
def __init__(self, model, color, battery_capacity):
# 实例属性,每个 Tesla 实例有自己的值
self.model = model
self.color = color
self.battery_capacity = battery_capacity # 电池容量
self.battery_level = 0 # 初始电量为0
def start(self):
# 行为:启动汽车
print(f"The {self.color} {self.model} is starting.")
def stop(self):
# 行为:停止汽车
print(f"The {self.model} has stopped.")
def charge(self, amount):
# 行为:给汽车电池充电
self.battery_level += amount
print(f"Charged {amount}kWh. Current battery level is {self.battery_level}kWh.")
def __str__(self):
# 特殊方法,返回对象的字符串表示
return f"{self.manufacturer} {self.model} in {self.color} color with {self.battery_capacity}kWh battery."
# 创建 Tesla 类的实例
my_tesla = Tesla(model="Model S", color="Red", battery_capacity=100)
# 访问实例属性
print(my_tesla.model) # 输出: Model S
print(my_tesla.color) # 输出: Red
# 调用实例方法
my_tesla.start() # 输出: The Red Model S is starting.
my_tesla.charge(50) # 充电50kWh
print(my_tesla) # 使用 __str__ 方法,输出: Tesla Model S in Red color with 100kWh battery.
(2)对象
对象是类的实例,通过调用类来创建。
以特斯拉汽车为例,对象就是特斯拉工厂生产出来的某辆的特斯拉汽车,它有唯一车辆标识码(VIN),同一个类创建的两个对象实例,可以理解为同一个特斯拉型号的两台车,虽然他们电动机型号相同,雨刮器型号相同,但他们却是两台不同的车。
下面例子my_tesla就是Tesla的实例,也就是对象。
# 创建 Tesla 类的实例
my_tesla = Tesla(model="Model S", color="Red", battery_capacity=100)
# 访问实例属性
print(my_tesla.model) # 输出: Model S
print(my_tesla.color) # 输出: Red
# 调用实例方法
my_tesla.start() # 输出: The Red Model S is starting.
my_tesla.charge(50) # 充电50kWh
print(my_tesla) # 使用 __str__ 方法,输出: Tesla Model S in Red color with
(3)属性
实例属性
实例属性是与具体对象相关的属性,在类的构造函数 init 中定义。
class Tesla:
# 类属性,所有 Tesla 实例共享
manufacturer = "Tesla"
def __init__(self, model, color, battery_capacity):
# 实例属性,每个 Tesla 实例有自己的值
self.model = model
self.color = color
self.battery_capacity = battery_capacity # 电池容量
self.battery_level = 0 # 初始电量为0
类属性
类属性是与类本身相关的属性,所有实例共享。
- 以特斯拉汽车为例,我们可以将类属性想象为所有特斯拉汽车共有的属性,例如公司总部的地址或者特斯拉汽车的设计理念。这些属性不会因每辆具体的特斯拉汽车而改变。
- 对象属性则是每辆特斯拉汽车独有的,比如每辆车的车牌号、颜色或者当前电量。这些属性因每辆车而异。
class Tesla:
# 类属性
company_address = "3500 Deer Creek Rd, Palo Alto, CA"
def __init__(self, license_plate, color, battery_level):
# 对象属性
self.license_plate = license_plate
self.color = color
self.battery_level = battery_level
# 创建特斯拉汽车的实例
tesla1 = Tesla("ABC 123", "红色", 100)
tesla2 = Tesla("DEF 456", "蓝色", 80)
# 访问类属性
print("特斯拉公司地址:", Tesla.company_address)
# 访问对象属性
print("第一辆特斯拉车牌号:", tesla1.license_plate)
print("第一辆特斯拉颜色:", tesla1.color)
print("第一辆特斯拉电量:", tesla1.battery_level)
# 改变对象属性
tesla1.battery_level = 85
print("更新后第一辆特斯拉电量:", tesla1.battery_level)
(4)方法
实例方法
实例方法是与具体对象相关的方法,第一个参数总是 self。
class Tesla:
# 类属性,用于存储所有特斯拉汽车的总数
total_cars = 0
def __init__(self, model, color, battery_capacity):
self.model = model
self.color = color
self.battery_capacity = battery_capacity
Tesla.total_cars += 1 # 每当创建新实例时,增加汽车总数
def charge(self, amount):
# 实例方法,给特定特斯拉汽车的电池充电
self.battery_level = amount
print(f"{self.model} charged to {self.battery_level} kWh")
# 创建几个 Tesla 实例
tesla1 = Tesla("Model S", "Red", 100)
tesla2 = Tesla("Model 3", "Blue", 75)
# 调用实例方法给特定特斯拉汽车充电
tesla1.charge(50) # 输出: Model S charged to 50 kWh
tesla2.charge(20) # 输出: Model 3 charged to 20 kWh
类方法
类方法是与类相关的方法,第一个参数总是 cls。使用 @classmethod 装饰器。
class Tesla:
# 类属性,用于存储所有特斯拉汽车的总数
total_cars = 0
def __init__(self, model, color, battery_capacity):
self.model = model
self.color = color
self.battery_capacity = battery_capacity
Tesla.total_cars += 1 # 每当创建新实例时,增加汽车总数
@classmethod
def get_total_cars(cls):
# 类方法,返回特斯拉汽车的总数
return cls.total_cars
def charge(self, amount):
# 实例方法,给特定特斯拉汽车的电池充电
self.battery_level = amount
print(f"{self.model} charged to {self.battery_level} kWh")
# 创建几个 Tesla 实例
tesla1 = Tesla("Model S", "Red", 100)
tesla2 = Tesla("Model 3", "Blue", 75)
# 调用类方法获取特斯拉汽车的总数
print(Tesla.get_total_cars()) # 输出: 2
# 调用实例方法给特定特斯拉汽车充电
tesla1.charge(50) # 输出: Model S charged to 50 kWh
tesla2.charge(20) # 输出: Model 3 charged to 20 kWh
在这个示例中,Tesla 类有一个类属性 total_cars 来跟踪所有创建的特斯拉汽车的总数。每当创建一个新的 Tesla 实例时,这个总数就会增加。get_total_cars 是一个类方法,它返回当前的汽车总数。我们使用 @classmethod 装饰器来定义它,并使用 cls 参数来访问类属性。
charge 是一个实例方法,它属于 Tesla 类的每个实例。它用于给特定特斯拉汽车的电池充电,并打印当前的电量。我们使用 self 参数来访问和修改实例属性。
类方法和实例方法在面向对象编程中非常有用,它们允许你定义与类或实例相关的行为,并且可以根据需要进行适当的操作。
2.面向对象的三大特性
支持面向对象的编程语言一般都有继承、封装、多态几个特性,我们先介绍一下它们对于程序员构建软件系统有哪些好处。
(1). 继承(Inheritance)
实际意义:
- 继承允许新创建的类(子类)继承现有类(父类或超类)的属性和方法,而无需重新编写代码。
- 子类可以扩展或修改父类的行为。
解决的问题:
- 代码重用:减少代码重复,提高开发效率。
- 层次结构:创建清晰的类层次结构,使得代码更加模块化。
- 扩展性:方便地对现有功能进行扩展。
概念理解:
在面向对象编程中,继承是一种机制,允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。这在特斯拉汽车的自动驾驶系统的迭代中得到了很好的体现。
1. 基类 - 基础自动驾驶功能:
假设我们有一个基类叫做 AutoDriveBase,它包含了所有特斯拉汽车共有的基本自动驾驶功能,如车道保持、自适应巡航控制等。
2. 子类 - 特定车型的自动驾驶功能:
针对不同车型,比如Model S、Model 3、Model Y,我们可以创建继承自 AutoDriveBase 的子类,如 ModelSAutoDrive、Model3AutoDrive、ModelYAutoDrive。这些子类继承了基础自动驾驶功能,并可以添加特定于车型的特性和改进。
3.进化 - 硬件版本迭代:
随着特斯拉自动驾驶硬件的迭代,比如从HW 2.0到HW 3.0,每个硬件版本都可以看作是一个继承链。新的硬件版本类继承自旧版本,并增加新的功能或提升性能。
代码示例:
class AutoDriveBase:
def __init__(self):
self.lane_keeping = True
self.cruise_control = True
def drive(self):
print("Driving with basic lane keeping and cruise control.")
class ModelSAutoDrive(AutoDriveBase):
def drive(self):
# 扩展基类的drive方法
print("Model S is driving with advanced autopilot features.")
class Model3AutoDrive(AutoDriveBase):
def drive(self):
# 重写基类的drive方法
print("Model 3 is driving with standard autopilot features.")
# 创建不同车型的实例并调用drive方法
model_s = ModelSAutoDrive()
model_s.drive() # 输出: Model S is driving with advanced autopilot features.
model_3 = Model3AutoDrive()
model_3.drive() # 输出: Model 3 is driving with standard autopilot features.
在这个继承示例中,AutoDriveBase 类定义了所有特斯拉汽车共有的基本自动驾驶功能。ModelSAutoDrive 和 Model3AutoDrive 类继承自 AutoDriveBase 类,并重写或扩展了 drive 方法以反映不同车型的自动驾驶功能。
(2).封装(Encapsulation)
实际意义:
- 封装是将对象的数据(属性)和行为(方法)组合在一起,并隐藏内部细节,只暴露出一个可以被外界访问的接口。
- 通过访问修饰符(如public, private, protected)控制对类成员的访问。
解决的问题:
- 数据保护:防止对象的内部数据被外部代码直接访问和修改,从而保护数据的完整性。
- 接口简化:只暴露必要的操作,隐藏实现细节,简化了调用者的代码。
概念理解
封装是指将对象的数据(属性)和行为(方法)组合在一起,并隐藏内部细节,只暴露出一个可以被外界访问的接口。在特斯拉汽车中:
1.属性隐藏:
特斯拉汽车的电池管理系统(BMS)是封装的一个例子。电池的状态、温度和充电周期等信息被封装在BMS内部,驾驶员和乘客不需要了解这些复杂细节,只通过仪表板获得必要的信息,如剩余电量和续航里程。
2.方法封装:
特斯拉汽车的自动驾驶功能也是封装的体现。所有的传感器数据处理、路径规划和决策逻辑都被封装在自动驾驶软件内部。用户通过简单的接口(如启动/停止按钮)与之交互,而不需要了解背后的复杂算法。
3.用户界面:
特斯拉的中控触摸屏是封装的一个直观例子。所有的车辆控制功能,如空调、导航、媒体播放等,都被设计成直观的图标和菜单,用户可以轻松操作而无需深入了解系统的工作原理。
代码示例:
class BatteryManagementSystem:
def __init__(self, battery_capacity):
self.__battery_capacity = battery_capacity # 私有属性,表示电池容量
self.__battery_state = 'normal' # 私有属性,表示电池状态
def get_remaining_capacity(self):
# 公开方法,返回剩余电量
return self.__battery_capacity
def charge(self):
# 充电方法,简化电池充电过程
self.__battery_state = 'charging'
def discharge(self):
# 放电方法,简化电池放电过程
if self.__battery_state != 'discharging':
self.__battery_state = 'discharging'
# 使用封装的电池管理系统
bms = BatteryManagementSystem(75) # 假设电池容量为75kWh
print(bms.get_remaining_capacity()) # 输出: 75
bms.charge() # 执行充电操作
在这个封装示例中,BatteryManagementSystem 类封装了电池的容量和状态,通过公共方法 get_remaining_capacity, charge, 和 discharge 来访问和修改这些私有属性。
(3).多态(Polymorphism)
实际意义:
- 多态允许同一个接口接受不同的数据类型。
- 方法重载(编译时多态)和方法重写(运行时多态)是多态的两种形式。
解决的问题:
- 接口统一:不同的对象可以通过相同的接口调用方法,但具体的行为会根据对象的实际类型来确定。
- 灵活性:代码可以对不同类型的对象执行不同的操作,而不需要修改调用代码。
- 解耦:降低不同模块之间的耦合度。
概念理解
多态是指允许不同类的对象对同一消息做出响应的能力,但响应的具体实现取决于对象的实际类型。在特斯拉汽车中:
接口重用:
特斯拉汽车的充电接口可以作为一个多态的例子。无论是Model S、Model 3还是Model Y,它们都使用相同的充电接口,但是内部的电池技术和充电策略可能不同。充电站通过统一的接口为所有车型提供电力,但每款车型根据自身的特性处理充电过程。
方法重写:
特斯拉的自动驾驶系统中,不同车型可能重写了相同的方法来适应其特定的硬件配置。例如,adjustSpeed() 方法在Model S中可能考虑了不同的空气动力学特性,而在Model 3中则可能考虑了不同的重量分布。
传感器数据处理:
特斯拉汽车使用多种传感器来收集环境数据。尽管所有车型都使用processSensorData() 方法来处理这些数据,但每种车型可能会根据自身的传感器配置和性能需求来重写这个方法,实现不同的数据处理逻辑。
通过封装,特斯拉汽车确保了系统的安全性和易用性,用户可以方便地与复杂的系统交互而无需了解其内部工作原理。而多态则允许特斯拉为不同车型提供定制化的响应和行为,提高了系统的灵活性和可扩展性。
代码示例:
class BatteryManagementSystem:
def __init__(self, battery_capacity):
self.__battery_capacity = battery_capacity # 私有属性,表示电池容量
self.__battery_state = 'normal' # 私有属性,表示电池状态
def get_remaining_capacity(self):
# 公开方法,返回剩余电量
return self.__battery_capacity
def charge(self):
# 充电方法,简化电池充电过程
self.__battery_state = 'charging'
def discharge(self):
# 放电方法,简化电池放电过程
if self.__battery_state != 'discharging':
self.__battery_state = 'discharging'
# 使用封装的电池管理系统
bms = BatteryManagementSystem(75) # 假设电池容量为75kWh
print(bms.get_remaining_capacity()) # 输出: 75
bms.charge() # 执行充电操作
在这个多态示例中,TeslaCar 类定义了一个 adjust_speed 方法,而 ModelS 和 Model3 类重写了这个方法,分别考虑了空气阻力和重量分布。这展示了多态性,即使用相同的方法名,但根据不同的对象类型执行不同的代码。
3.其他
(1).属性装饰器
属性装饰器用于将方法转换为只读属性。
在Python中,使用属性装饰器 @property 创建只读属性后,如果你希望允许属性被赋值,可以通过定义一个 setter 方法来实现。Setter 方法允许你指定当属性被赋值时应执行的代码,例如进行验证或修改赋值的值。
定义只读属性:
使用 @property 装饰器将一个方法变为只读属性。
以特斯拉为例:假设特斯拉汽车有一个属性 battery_level 表示当前电量百分比,我们希望这个属性是只读的,但允许通过一个方法 charge 来增加电量。
class Tesla:
def __init__(self, battery_capacity):
self._battery_level = 0 # 私有属性,初始电量为0
self.battery_capacity = battery_capacity # 电池容量
@property
def battery_level(self):
# 只读属性,返回当前电量百分比
return self._battery_level / self.battery_capacity * 100
def charge(self, amount):
# 充电方法,增加电量,但不能直接设置 battery_level
self._battery_level = min(self._battery_level + amount, self.battery_capacity)
print(f"Charged by {amount}kWh. New battery level is {self._battery_level}kWh.")
# 创建特斯拉汽车实例
my_tesla = Tesla(battery_capacity=100) # 假设电池容量为100kWh
# 访问只读属性 battery_level
print(my_tesla.battery_level) # 输出: 0
# 通过 charge 方法改变电量
my_tesla.charge(50)
print(my_tesla.battery_level) # 输出: 50.0
# 尝试直接赋值将引发错误
# my_tesla.battery_level = 75 # 将引发错误: can't set attribute
在这个示例中,battery_level 是一个只读属性,通过 @property 装饰器定义。我们不能直接给 battery_level 赋值,因为这将引发一个错误。相反,我们提供了一个 charge 方法来间接改变电量。尝试直接给 battery_level 赋值会导致 AttributeError,因为属性装饰器将阻止这一操作。
这种模式在你需要控制属性如何被访问和修改时非常有用,它提供了一种封装属性并保护其不被外部直接修改的方式。
定义 setter 方法:
如果你想允许属性被赋值,可以定义一个带有 @<property_name>.setter 装饰器的方法。
class Tesla:
def __init__(self, battery_capacity):
self._battery_capacity = battery_capacity # 电池容量
self._current_battery_level = 0 # 当前电量,初始为0
@property
def battery_level(self):
# 只读属性,返回当前电量百分比
return self._current_battery_level / self._battery_capacity * 100
@battery_level.setter
def battery_level(self, value):
# 当尝试设置 battery_level 时调用
if 0 <= value <= 100:
# 确保电量百分比在0到100之间
self._current_battery_level = value / 100 * self._battery_capacity
else:
raise ValueError("Battery level must be between 0 and 100 percent")
def charge(self, amount):
# 充电方法,增加电量
self.battery_level = self.battery_level + amount # 使用 setter 方法
# 创建特斯拉汽车实例
my_tesla = Tesla(battery_capacity=100) # 假设电池容量为100kWh
# 正常设置电量百分比
my_tesla.battery_level = 30 # 设置电量为30%
print(my_tesla.battery_level) # 输出: 30.0
# 尝试设置无效的电量百分比
try:
my_tesla.battery_level = 150 # 这将引发 ValueError
except ValueError as e:
print(e) # 输出: Battery level must be between 0 and 100 percent
# 使用 charge 方法增加电量
my_tesla.charge(20) # 从当前电量增加20kWh
print(my_tesla.battery_level) # 输出: 50.0
在这个例子中,我们添加了 battery_level 属性的 setter 方法。如果尝试设置的电量百分比在0到100之间,setter 方法会接受这个值,并相应地更新私有属性 _current_battery_level。如果尝试设置的电量百分比超出了这个范围,setter 方法会抛出一个 ValueError 异常。
通过这种方式,setter 方法允许你在赋值时添加逻辑来保护属性值的有效性,确保对象的状态始终保持一致。
(2).类的组合
类的组合是面向对象编程中的一种设计原则,它允许创建更复杂的对象结构,通过将一个类的对象作为另一个类的属性来使用。这种组合关系创建了类之间的“has-a”关系,即一个对象“拥有”另一个对象。
以特斯拉汽车和它的电池系统为例,我们可以将电池系统视为一个独立的类,而特斯拉汽车是另一个类,它“拥有”一个电池系统对象作为其属性。
类的组合示例:
1.电池系统类(BatterySystem):
这个类可以包含电池的容量、当前电量、充电和放电的方法等。
2.特斯拉汽车类(TeslaCar):
这个类可以包含汽车的制造商、型号、颜色等属性,以及驾驶、停车等方法。
特斯拉汽车类将包含一个电池系统类的对象作为其属性,表示每辆特斯拉汽车都有一个电池系统。
class BatterySystem:
def __init__(self, capacity):
self.capacity = capacity
self.current_charge = capacity
def charge(self, amount):
self.current_charge += amount
print(f"Charged the battery by {amount}kWh. Total charge is now {self.current_charge}kWh.")
def discharge(self, amount):
self.current_charge -= amount
if self.current_charge < 0:
self.current_charge = 0
print("Battery is empty.")
else:
print(f"Discharged the battery by {amount}kWh. Remaining charge is {self.current_charge}kWh.")
class TeslaCar:
def __init__(self, model, color, battery):
self.model = model
self.color = color
self.battery = battery # 这里battery是BatterySystem的一个实例
def drive(self, distance):
# 假设每公里消耗0.2kWh电量
energy_needed = distance * 0.2
if self.battery.current_charge >= energy_needed:
self.battery.discharge(energy_needed)
print(f"Driven {distance}km. Remaining battery charge is {self.battery.current_charge}kWh.")
else:
print("Not enough battery charge to drive this distance.")
# 创建电池系统实例
tesla_battery = BatterySystem(capacity=100) # 电池容量为100kWh
# 创建特斯拉汽车实例,并将电池系统作为其属性
my_tesla = TeslaCar(model="Model S", color="红色", battery=tesla_battery)
# 使用特斯拉汽车实例的方法
my_tesla.drive(50) # 驾驶50公里
在这个示例中,BatterySystem 类创建了一个电池系统对象,它有充电和放电的方法。TeslaCar 类创建了一个特斯拉汽车对象,它拥有一个 BatterySystem 类型的属性 battery。这展示了类的组合,即特斯拉汽车类“拥有”一个电池系统对象,并且可以通过该对象执行电池相关的操作。
(3).特殊方法(魔术方法)
在Python中,以双下划线(__)开头和结尾的方法被称为特殊方法(或魔术方法)。这些方法通常不需要直接调用,而是由Python解释器或类在特定的情况下自动调用。以下是一些常见的特殊方法:
- _init_(self, …):
- 构造器方法。当一个实例被创建时,自动调用此方法来初始化对象。
class MyClass:
def __init__(self, value):
self.value = value
- _del_(self):
- 析构器方法。当对象被销毁时,自动调用此方法来执行清理操作。
class MyClass:
def __del__(self):
print("Object is being destroyed")
- _str_(self):
- 当使用print()函数或str()时,自动调用此方法来返回对象的字符串表示。
class MyClass:
def __str__(self):
return "MyClass with value " + str(self.value)
- _repr_(self):
- 提供官方的字符串表示,通常可以用来重新创建该对象。在交互式解释器中使用时很有用。
class MyClass:
def __repr__(self):
return "MyClass(" + repr(self.value) + ")"
- _len_(self):
- 当使用len()函数时,自动调用此方法来返回容器类型的长度。
class MyCollection:
def __init__(self):
self.items = []
def __len__(self):
return len(self.items)
- _getitem_(self, key):
- 用于实现对象的索引访问,如obj[key]。
class MyCollection:
def __getitem__(self, index):
return self.items[index]
- _setitem_(self, key, value):
- 用于实现对象的索引赋值,如obj[key] = value。
class MyCollection:
def __setitem__(self, index, value):
self.items[index] = value
- _delitem_(self, key):
- 用于实现对象的索引删除,如del obj[key]。
class MyCollection:
def __delitem__(self, index):
del self.items[index]
- _iter_(self):
- 返回对象的迭代器,用于实现迭代操作,如for x in obj。
class MyCollection:
def __iter__(self):
return iter(self.items)
- _next_(self):
- 用于迭代器的next()方法,返回容器的下一个元素。
class MyIterator:
def __init__(self, items):
self.index = 0
self.items = items
def __next__(self):
if self.index < len(self.items):
item = self.items[self.index]
self.index += 1
return item
else:
raise StopIteration
- _getattr_(self, name):
- 当访问一个不存在的属性时,自动调用此方法。
class MyClass:
def __getattr__(self, name):
if name == "value":
return 42
raise AttributeError(f"{name} not found")
- _setattr_(self, name, value):
- 当设置一个属性的值时,自动调用此方法。
class MyClass:
def __setattr__(self, name, value):
if name == "value":
self.__dict__["value"] = value * 2
else:
super().__setattr__(name, value)