《《Python Python 程序程序设计设计基基础础(微(微课课版)版)》》
面向对象程序设计
任务导入
Python 作为面向对象编程语言
Python 是一门真正面向对象的高级动态编程语言,支持面向对象的基本特性。在使用 Python 编写程序时,应该使用面向对象的思想来分析问
题,抽象出项目不同模块的共同特点。相较于其他编程语言, Python 中对象的概念更加广泛。
知识目标
理解面向对象的思想
理解类与对象
掌握属性与方法
掌握继承与多态
能力目标
掌握面向对象的程序设计方法
掌握类与对象的创建和使用方法
掌握实例属性与对象方法的使用方法
掌握继承与多态的使用方法
面向对象编程的基本概念
起源与发展
20 世纪60 年代
面向对象程序设计起源于Simula 语言
理论基础
软件工程、结构化程序设计、数据抽象、信息隐藏、知识表示及并行
处理等多种理论的积累和发展
现代应用
如今的C++ 、Java、Python 、C# 和Ruby等主流编程语言都采用面
向对象思想
核心思想
对问题空间进行自然分割:以更接近人类思维的方式建立问题域模型
对客观实体进行结构模拟:使设计的软件能直接描述现实世界
融合属性与服务:对象作为信息实体的统一概念,把属性和服务融为一
体
支持重用与维护:通过类、对象、封装、继承、多态等机制构造软件系
统
面向对象编程的优势
软件设计更加灵活 更好支持代码复用 支持设计复用 提高代码可读性 增强代码可扩展性
面向过程 vs 面向对象
面向过程
基本特点
优缺点
自上而下的设计方法
程序= 数据结构+ 算法
数据与数据处理过程分开
以函数为中心
✓ 易于理解和掌握
✗ 不利于程序维护
✗ 代码复用率低
✗ 可扩展性差
✗ 处理复杂情况困难
面向对象
基本特点
优缺点
自下而上的设计方法
数据与数据处理当作一个整体(对象)
以对象为核心
从静态(属性)和动态(方法)两方面考虑
✓ 数据抽象化,减少外部干扰
✓ 通过继承减少冗余代码
✓ 降低代码出错率,提高可维护性
✓ 便于控制软件复杂度
为何面向对象更适合复杂系统开发?
面向对象将软件系统分割为若干个相互独立的部分,更好地设计和实现系统。通过封装、继承和多态等特性, 面向对象程序设计提供了更好的可重用性、可扩展性
和可维护性,特别适合生态保护这类复杂系统的模拟与开发。
面向对象的优势
数据抽象化
将数据抽象化,可在外部接口不改变的前提下改变内部实现,减少或避免外
部干扰。这使得系统更加灵活,便于维护和更新。
代码复用
通过继承可大幅度减少冗余代码,降低代码出错率,提高代码利用率与软件
可维护性。继承机制使得代码模块可以被多个类共享使用。
系统模块化
将对象按照同一属性和行为划分为不同的类,可将软件系统分割为若干个相
互独立的部分,便于控制软件复杂度,提高系统的可维护性和可扩展性。
全面的问题分析视角
以对象为核心,开发人员可从静态(属性)和动态(方法)两个方面考虑问
题,更好地设计、实现系统。这种双重视角使得系统设计更加完整和合理。
类与对象的基本概念
类(Class )
类是对一组具有相同属性对象(实例)的抽象。
类是模板,抽象描述了属于该类的全部对象共有的属性和操作
类是多个对象(实例)的综合抽象
对象(Object )
对象是类的个体实物,是类的具体实例。
对象是类的具体表现
对象具有类定义的所有特征和行为
类与对象的关系
抽象与具体的关系
类与对象的关系是抽象与具体的关系
类定义了对象的结构(属性)和行为(方法)
一个类可以创建多个不同的对象实例
类的定义与构成
类的四个主要组成部分
类的名称
属性结构
与其他类的关系
外部操作类的操作界面
Python 中定义类的语法
使用class 关键字定义类
类名后跟冒号,换行后以缩进控制Python 逻辑关系
根据编程规范,类名首字母需要大写
保持整个系统设计与实现的风格一致
类定义示例
class Forest:
# 类的构造函数
def __init__(self, name, area):
= name
= area
= []
# 定义类的方法
def add_tree(self, tree):
(tree)
return "Added tree to forest"
对象的特征与创建
对象的基本特征
现实世界中客观存在的事物称为对象,如将对象数字化,则具有如下特征:
唯一标识
有一个名称,用来唯一标识对象
状态描述
有一组状态,用来描述其特征
功能操作
有一组操作,用来实现其功能
对象的创建与使用
类是抽象的,要使用类定义的功能,必须将其实例化,即创建类的对象:
可通过 对象名.成员
class Tree:
def __init__(self, name, height):
= name
= height
def grow(self):
+= 1
pine = Tree("松树", 10)
()
print()
属性与方法
属性分类
类型 定义位置 访问方式
实例属性 __init__() 中 self 前缀
类属性 类中方法外 类名或对象名
class Forest:
area = 100 # 类属性
def __init__(self, name):
= name # 实例属性
构造函数__init__() 用于初始化对象属性,在创建对象时自动执行。每个对象都有
自己的构造函数。
公有属性与私有属性
公有属性
• 可在类内部和外部使用
• 无特殊命名规则
• 通过对象名可直接访问
私有属性
• 以双下划线开头命名
• 仅能在类内部访问
• 外部需通过公有方法访问
class Tree:
def __init__(self):
= 10 # 公有属性
self.__age = 50 # 私有属性
def get_age(self): # 公有方法
return self.__age
tree = Tree()
print() # 正常访问
print(_age()) # 通过方法访问
# print(tree.__age) # 错误!不能直接访问
Python 允许开发者动态地为类和对象增加成员,这是Python 与其他面向对象语言的重要区别之一。
属性与方法
自定义属性特殊属性
特殊属性 含义
obj._dict 对象的属性字典
obj._class 对象所处的类
class. name 类的名称
class._doc 类的文档字符串
class._module_ 类定义所在的模块
class._bases 类的基类元组(多继承)
class._base 类的基类
class. mro 类层次结构
class._subclasses () 子类列表
Python 是动态语言,因此 Python 支持为对象动态添加实例变量,
为该实例的新变量进行赋值;也可以动态删除实例变量(使用 del
语句进行删除)。
class student:
pass
s=student()
='swimming'
print() #输出'swimming'
print(s.__dict__) #输出{'skill': 'swimming'}
del
print() #报错 AttributeError: 'student'
object has no attribute 'skill'
在上述程序中,首先为 student 对象 s 动态添加了一个实例变量 skill。
这里只要为实例变量赋值就是添加了一个实例变量。接下来调用del
语句删除了对象s的实例变量skill。
Python 类的特殊方法
Python 类的特殊方法又被称为魔术方法,它是以双下画线包裹一个词的形式出现的,如前面学过的__init__方法和__del__方法。特殊方法不仅
可以实现构造和初始化,而且可以实现比较、算法运算等功能。表 9-2 列出了一些常用的特殊方法,其中具体的运算符在相应方法的括号中。
分类 方法
字符串字节类 __repr__、__str__、__format__、__bytes__
转换成数字 __abs__、__bool__、__complex__、__float__、__hash__、__index__
仿集合类 __len__、__getietm__、__setitem__、__delitem__、__contains__
迭代循环 __iter__、__reversed__、__next__
仿可调用 __call__
上下文管理 __enter__、__exit__
实例创建与销毁 __new__、__init__、__del__
属性管理 __getattr__、__getattribute__、__setattr__、__delattr__、__dir__
属性描述 __get__、__set__、__delete__
类服务 __prepare__、__instancecheck__、__subclasscheck__
一元运算符 __neg__、__pos__、__abs__
比较运算符 __lt__(<)、__le__(<=)、__eq__(= =)、__ne__(!=)、__gt__(>)、__ge__(>=)
算术运算符
__add__(+)、__sub__(-)、__mul__(*)、__truediv__(/)、__floordiv__(//)、__mod__(%)、
__divmod____、pow__(**)、__round__
@property 装饰器
面向对象编程封装的思想原则上要求类包含的属性是隐藏的,只允许通过类提供的方法来间接实现对类属性的访问和操作。Python 中可以定
义私有属性,然后定义相应的函数来访问私有属性。使用@property 装饰器来装饰这些函数,这样程序就可以把函数当作属性来访问,原来
需要写 ( ),加上它以后只要写 ,从而提供更友好的访问方式。@property 装饰器默认只提供一个只读属性,如果需要,可
以使用包含读(或写)类属性的 getter、setter 和 deleter 装饰器。
【例 9-6】@property 装饰器示例
class Goods:
def __init__(self,price):
self.__price = price
@property #定义 property 属性
def price(self):
return self.__price
g = Goods(100)
result = #自动执行@property 装饰器的 price 方法,并获取方法的返回值
print(result)
程序的运行结果如下:
100
对象方法的分类与使用
实例方法
通常情况下,在类中定义的方法默认都是实例方法。
def 方法名(self,[形参列表]):
函数体
它要包含一个 self 参数
self 总是指向调用该方法的对象
当该实例方法是构造方法时,引用该构造方法
正在初始化的对象
当该实例方法是普通实例方法时,引用调用该
方法的对象
类方法
Python 类方法就是类本身的方法
@classmethod
def 方法名(cls,[形参列表]):
函数体
类方法不对特定实例进行操作
最少也要包含一个参数,命名为 cls
Python 会自动将类本身绑定给 cls 参数
使用@classmethod 装饰器进行修饰
构造方法
._init__( )类构造方法
def __init__(self,[形参列表]):
函数体
称为构造方法(或构造函数)
构造方法就是类在实例化时执行的方法
类中的构造方法不是必须要写的
对象方法的分类与使用
私有方法
名称以两个下画线开始,不能通过对象名直接访问
class Demo:
def __private_method(self):
# 内部实现
def public_method(self):
self.__private_method()
属于对象,每个对象都有自己的私有方法
可访问属于类和对象的成员
只能在属于对象的方法中调用
或通过Python 提供的特殊方式调用
公有方法
公开使用,可以通过对象名直接调用
class Demo:
def public_method(self):
# 方法实现
obj = Demo()
_method() # 直接调用
属于对象,每个对象都有自己的公有方法
可访问属于类和对象的成员
可在类的内部使用
也可在类的外部程序中使用
静态方法
可通过类名和对象名调用
class Demo:
@staticmethod
def static_method():
# 方法实现
_method() # 类调用
不属于对象,所有对象共享一个静态方法
不能直接访问属于对象的成员
只能访问属于类的成员
适用于与实例无关的功能实现
方法的重载是指定义多个同名的方法。由于 Python 是动态语言,方法的参数在声明时不需要知道类型,而是通过传值调用时确定
参数类型;所以 Python 对象的方法不需要重载,定义一个方法即可实现多种调用。如果类体中定义了多个方法,但只有最后一个
方法有效,那么不建议定义重名的方法。
【例 9-15】方法重载示例
class Myclass:
def __init__(self, name):
= name
def say_hi(self, friend=None):
= friend
if friend==None:print("hello!")
else: print( + " say hello to " + )
c = Myclass("小明") #创建对象
_hi() #通过对象调用方法,无参数
_hi("小红") #通过对象调用方法,带参数
方法的重载
程序的运行结果如下:
hello!
小明 say hello to 小红
多态
•多态指面向对象程序执行时,相同的信息可能会发送给多个不同类别的对象,系统依据对象所属的类别,引发对应类别的方法而产生不同
的行为。
•Python是动态语言,可以不检查类型但成功调用实例方法,只要方法存在、属性正确即可实现调用,这是Python语言与静态语言(如
Java)最大的差别之一。
示例:定义父类ParentClass,派生出3 个子类Child_B、Child_C、
Child_D。前面两个子类Child_B 和Child_C 对父类中的方法进行了
重写。创建一个 ParentClass 类的对象,然后声明一个变量 a,再
将变量 a 与对象联系起来。变量 a 是没有类型的,它的类型取决于
其关联的对象。后面依次创建 3 个子类的对象,依次赋值给变量 a
,也就是和 a 关联起来。使用 ( )来调用 prt( )方法,变量
a 指向哪个对象,就是调用哪个对象的 prt( )方法。例如,当 a 指
向子类 Child_B 的对象时,调用的就是 Child_B 中的 prt( )方法,
这样输出的就是"Child_B"。注意,子类 Child_D 并没有重写 prt(
)方法,因此当 a 指向子类 Child_D的对象时,就会去调用父类
ParentClass 中的 prt( )方法。
class ParentClass:
def prt(self):
print("ParentClass")
class Child_B(ParentClass):
def prt(self):
print("Child_B")
class Child_C(ParentClass):
def prt(self):
print("Child_C")
class Child_D(ParentClass):
pass
a = ParentClass()
()
a = Child_B()
()
a = Child_C()
()
a = Chil
程序的运行结果如下:
ParentClass
Child_B
Child_C
ParentClass
继承的概念与实现
继承的概念
在面向对象程序设计中,当需要定义一个类时,可通过从已有的类中继承来
实现。新定义的类称为子类或派生类,而被继承的类称为基类、父类或者超
类。
继承使得子类可以获得父类的公有成员,但不能继承父类的私有成员。
父子类关系
父类 ( 基类/超类)
子类 ( 派生类)
继承的语法
class 父类名(Object):
# 父类定义...
class 子类名(父类名):
# 子类定义...
# 父类必须继承于Object类,否则子类将无法使用super()等函数
调用父类方法
子类可通过以下两种方式调用父类方法:
1. 使用super() 函数
super().父类方法名(参数)
2. 使用父类名
父类名.父类方法名(self, 参数)
多重继承
使用注意事项
在多重继承的程序设计中,需注意super() 函数调用和父类名调用
两种方式不可混合使用,否则有可能导致访问序列紊乱。
多重继承概念
Python 支持多重继承。若子类有多个父类,则父类名需要全部写
在子类声明的括号里,以实现多重继承。
语法格式
class 子类名(父类1, 父类2, ...):
pass # 类体为空时使用
pass 关键字
Python 提供了pass关键字,它类似于空语句,可在类、函数定义
和分支结构等程序中使用。
父类方法访问
Python 提供了两种访问父类函数的方式:
super() 函数调用
super().method_name()
父类名调用
_name(self)
面向对象编程的模块化优势
模块化设计示例
生态模块 植被模块
动物模块
气候模块
模块化设计原理
面向对象编程提倡将软件系统分解成多个模块,每个模块负责实现特定的
功能。每个模块都可以看作一个对象,它封装了数据和行为,并提供了一
个清晰的接口供其他模块使用。
模块化的优势
降低系统复杂度:通过模块化开发,可以将复杂系统分解为易于理解
和管理的小模块
提高代码组织性:每个模块专注于特定功能,使代码结构更清晰
接口明确:模块间通过定义良好的接口进行通信,减少耦合
独立开发与测试:各模块可以独立开发、测试和维护,提高开发效率
代码的可复用性
通过继承提高代码复用
继承可以大幅度减少冗余代码,降低代码出错率,提高代码利用率与软件可
维护性。
"通过继承,可以创建新的类并重用现有类的代码,从而减少代码冗余。"
继承关系示意
子类 A 子类 B 子类 C
基类
可重用代码
通过多态增强灵活性
多态允许以统一的方式处理不同类型的对象,提高代码的灵活性和可扩展
性。
"通过多态,可以以统一的方式处理不同类型的对象,从而提高代码的灵活性和
可扩展性。"
可复用性的优势
减少冗余代码 - 避免重复编写相似功能的代码
降低错误率 - 集中维护核心代码,减少出错机会
提高代码利用率 - 一次编写,多处使用
增强可维护性 - 模块化设计使系统更易于维护
增强可扩展性 - 更容易添加新功能而无需修改现有代码
增强代码的可维护性
面向对象编程提供了一种组织和管理代码的方式,使代码更加模块化,增强了代码的可维护性和可扩展性。 通过封装、继承和多态三大核心特
性,OOP可以使系统更容易理解、修改和扩展。
封装
将数据抽象化,隐藏内部实现细节
维护性提升:
可在外部接口不改变的前提下改变内部实现
减少或避免外部干扰
提高代码的安全性和稳定性
简化系统复杂度,使接口清晰明了
继承
通过继承机制重用现有类的代码
维护性提升:
大幅度减少冗余代码
降低代码出错率
提高代码利用率
集中管理共享功能,一处修改多处生效
形成清晰的类层次结构,便于理解和扩展
多态
不同对象对同一消息做出不同反应
维护性提升:
以统一的方式处理不同类型的对象
提高代码的灵活性和可扩展性
降低模块间的耦合度
简化系统设计,增强系统的可维护性
便于添加新的类而无需修改现有代码
通过这三大特性,面向对象编程使开发人员可从静态(属性)和动态(方法)两个方面考虑问题,更好地设计和实现系统。
总结与展望
OOP 在软件开发中的价值
面向对象编程在软件系统开发中提供了一种组织和管理代码的有效方
式,使系统更加模块化、灵活和可扩展。
模块化设计:将软件系统分解为多个独立对象,每个对象负责特定
功能
层次化模拟:通过继承关系模拟现实中事物的分类层次
动态交互:利用多态实现事物之间的复杂交互关系
OOP 提高软件质量的作用
降低系统复杂度:通过模块化开发降低系统的整体复杂度
提高代码复用性:通过继承机制减少代码冗余,提高代码利用率
增强可维护性:封装机制使内部实现可以在不影响外部接口的情况
下修改
提升可扩展性:面向对象设计使系统能够更容易地添加新功能
未来展望
随着人们对软件需求的不断增长,面向对象编程将在更复杂的软件设计、软件开发等领域发挥更大作用,为新一代信息技术的不断发展提供有力的技术支持。