Python 语言高级特性的相关知识,内容包括什么是对象、类的定义与调用、类定义中的特殊方法、自定义对象的排序、类的继承、例外处理、推导式、生成器函数、上机练习。
一、什么是对象
1. 对象的基本概念
“万物皆对象”
- Python中的所有事物都是以对象形式存在,从简单的数值类型,到复杂的代码模块,都是对象
对象(object)
- 既表示客观世界问题空间中的某个具体事物,又表示软件系统解空间中的基本元素
对象 = 属性 + 方法
- 对象以id作为标识,既包含数据(属性),也包含代码(方法),是某一类具体事物的特殊实例

2. 创建对象
对象是类的实例,是程序的基本单元
- 要创建一个新的对象,首先必须定义一个类,用以指明该类型的对象所包含的内容(属性和方法)
- 同一类(class)的对象具有相同的属性和方法,但属性值和id不同
对象的名称
- 赋值语句给予对象以名称,对象可以有多个名称(变量引用),但只有一个id例:a = complex(1, 2)
对象实现了属性和方法的封装,是一种数据抽象机制
- 提高了软件的重用性、灵活性、扩展性
3. 对象属性和方法的引用
引用形式
<对象名>.<属性名>
- 可以跟一般的变量一样用在赋值语句和表达式中
- 例: “abc”.upper()、(1+2j).real、(1+2j).imag
Python语言动态的特征,使得对象可以随时增加或者删除属性或者方法

4. 面向对象编程
面向对象编程(OOP)是一种程序设计范型,同时也是一种程序开发方法
- 程序中包含各种独立而又能互相调用的对象
- 每个对象都能接受、处理数据并将数据传递给其他对象
传统程序设计
- 将程序看作一系列函数或指令的集合
二、类的定义与调用
1. 什么是类
类(class)是对象的模版,封装了对应现实实体的性质和行为
实例对象(Instance Objects)是类的具体化
把类比作模具,对象则是用模具制造出来的零件
类的出现,为面向对象编程的三个最重要的特性提供了实现的手段
- 封装性、继承性、多态性
和函数相似,类是一系列代码的封装
- Python中约定,类名用大写字母开头,函数用小写字母开头,以便区分
2. 定义类
class语句
class <类名>:
<一系列方法的调用>
类的初始化
class <类名>:
def __init__(self, <参数表>):
def <方法名>(self, <参数表>):
__init__()是一个特殊的函数名,用于根据类的定
义创建实例对象,第一个参数必须为self
3. 调用类
<类名>(<参数>)
- 调用类会创建一个对象,(注意括号!)
obj = <类名>(<参数表>)
- 返回一个对象实例
- 类方法中的self指这个对象实例!
使用点(.)操作符来调用对象里的方法
t = turtle.Pen()
t.forward(100)
t.left(90)
…
4. 类的定义与调用

三、类定义中的特殊方法
1. 基本概念
特殊方法(special method)
- 也被称作魔术方法(magic method)
- 在类定义中实现一些特殊方法,可以方便地使用python中一些内置操作
- 所有特殊方法的名称以两个下划线(__)开始和结束
2. 构造与解构
对象构造器
__init__(self,[…)
- 对象的构造器,实例化对象时调用
析构器
__del__(self,[…)
- 销毁对象时调用

3. 算术运算
算术操作符
__add__(self,other):使用+操作符
__sub__(self,other):使用-操作符
__mul__(self,other):使用*操作符
__div__(self,other):使用/操作符
反运算
- 当左操作数不支持相应的操作时被调用
__radd__(self,other),__rsub__(self,other)
__rmul__(self,other),__rdiv__(self,other)
大小比较
__eq__(self,other):使用==操作符
__ne__(self,other):使用!=操作符
__lt__(self,other):使用<操作符
__gt__(self,other):使用>操作符
__le__(self,other):使用<=操作符
__ge__(self,other):使用>=操作符


4. 其他特殊方法
字符串操作
- 不仅数字类型可以使用像
+(__add__())和-(__sub__())的数学运算符,例如字符串类型可以使用+进行拼接,使用*进行复制
__str__(self):自动转换为字符串
__repr__(self):返回一个用来表示对象的字符串
__len__(self):返回元素个数
其它特殊方法参见课程网站
四、自定义对象的排序
1. 列表排序
列表方法sort()
- 对原列表进行排序,改变原列表内容
- 如果列表中的元素都是数字,默认按升序排序
- 通过添加参数reverse = True可改为降序排列
- 如果元素都是字符串,则会按照字母表顺序排列
2. 内置排序函数
通用函数sorted()
- 类似sort(),但返回的是排好序的列表副本,原列表内容不变
只有当列表中的所有元素都是同一种类型时,sort()和sorted()才会正常工作
3. 特殊方法
特殊方法__It__
- 由于Python的可扩展性,每种数据类型可以定义特殊方法
def __lt__(self, y)
- 返回True视为比y“小”,排在前
- 返回False视为比y“大”,排在后
只要类定义中定义了特殊方法__lt__,任何自定义类都可以使用x<y这样的比较
4. 可扩展的“大小”比较及排序
例子:Student
- 姓名name,成绩grade
按照成绩由高到低排序

构造一个列表,加入Student对象

直接调用列表sort方法
可以根据__lt__定义排序
〉 直接检验Student对象的大小
S[i]<s[j]
另外可以定义其它比较符
__gt__

重新定义__lt__方法,改为比较姓名
- 这样sort方法就能按照姓名来排序

五、类的继承
1. 类的继承机制
继承(inheritance)
- 如果一个类别A继承自另一个类别B,就把继承者A称为子类,被继承的类B称为父类、基类或超类
代码复用
- 利用继承可以从已有类中衍生出新的类,添加或修改部分功能
- 新类具有旧类中的各种属性和方法,而不需要进行任何复制


2. 子类与父类
定义
- 如果两个类具有“一般-特殊”的逻辑关系,那么特殊类就可以作为一般类的 “子类”来定义,从“父类”继承属性和方法
class <子类名>(<父类名>):
def <重定义方法>(self,…):
覆盖(Override)
- 子类对象可以调用父类方法,除非这个方法在子类中重新定义了
- 如果子类同名方法覆盖了父类的方法,仍然还可以调用父类的方法
子类还可以添加父类中没有的方法和属性

3. 关于self
在类定义中,所有方法的首个参数一般都是self
- self 的作用:在类内部,实例化过程中传入的所有数据都赋给这个变量
self实际上代表对象实例
<对象>.<方法>(<参数>)
等价于:
<类>.<方法>(<对象>, <参数>)
这里的对象就是self,如下图line81和82

六、例外处理
1. 代码错误类型
代码运行可能会意外各种错误
- 程序的逻辑错误、用户输入不合法等都会引发异常,但它们不会导致程序崩溃
- 可以利用python提供的异常处理机制,在异常出现时及时捕获并从内部消化掉
- 语法错误:SyntaxError
- 除以0错误:ZeroDivisionError
- 列表下标越界:IndexError
- 类型错误:TypeError
- 访问变量不存在:NameError
- 字典关键字不存在:KeyError
- 未知的变量属性:AttributeError
以上这些错误会引起程序中止退出,如果希望掌控意外,就需要在可能出错误的地方设置陷阱捕捉错误
2. 捕捉错误
try-except语句
try:
<检测语句>
except <错误类型> [as e]:
<处理异常>
try: # 为缩进的代码设置陷阱
except: # 处理错误的代码
- 针对不同异常可以设置多个except
try-finally语句
try:
<检测语句>
except <错误类型> [as e]:
<处理异常>
finally:
<语句块>
- finally: # 无论出错否,都执行的代码
- 如果try语句块运行时没有出现错误,会跳过except语句块执行finally语句块的内容
else语句
try:
<检测语句>
except <错误类型> [as e]:
<处理异常>
else:
<语句块>
- else: # 没有出错执行的代码

七、推导式
1. 什么是推导式
- 推导式是从一个或者多个迭代器快速简洁地创建数据结构的一种方法
- 将循环和条件判断结合,从而避免语法冗长的代码
- 可以用来生成列表、字典和集合
2. 推导式基本语法
列表推导式
[<表达式> for <变量> in <可迭代对象> if <逻辑条件>]
字典推导式
{<键值表达式>:<元素表达式> for <变量> in <可迭代对象> if <逻辑条件>}
集合推导式
{<元素表达式> for <变量> in <可迭代对象> if<逻辑条件>}

3. 生成器推导式
与推导式一样语法:
(<元素表达式> for <变量> in <可迭代对象> if<逻辑条件>)
返回一个生成器对象,也是可迭代对象,但生成器并不立即产生全部元素,仅在要用到元素的时候才生成,可以极大节省内存

除了通过生成器推导式创建生成器,还可以使用生成器函数
八、生成器函数
1. 什么是生成器
- 生成器(generator)是用来创建Python序列的一个对象
- 使用它可以迭代庞大的序列,且不需要在内存中创建和存储整个序列
- 通常生成器是为迭代器产生数据的,是迭代器的一种实现
2. 生成器函数
如果要创建一个比较大的序列,生成器推导式将会比较复杂,一行表达式无法容纳,这时可以定义生成器函数
生成器函数与普通函数
- 生成器函数的定义与普通函数相同,只是将return换成了yield
yield与return
- yield语句:立即返回一个值,下一次迭代生成器函数时,从yield语句后的语句继续执行,直到再次yield返回,或终止
- return语句:终止函数的执行,下次调用会重新执行函数
协同程序
- 可以运行的独立函数调用,函数可以暂停或挂起,
- 并在需要的时候从离开的地方继续或重新开始
函数even_number返回一个生成器对象

九、上机练习
1. 创建一个类People
- 包含属性name, city
- 可以转换为字符串形式(str)
- 包含方法moveto(self, newcity)
- 可以按照city排序
- 创建4个人对象,放到列表进行排序
2. 创建一个类Teacher
- 是People的子类,新增属性school
- moveto方法改为newschool
- 按照school排序
- 创建4个教师对象,放到列表进行排序
3. 创建一个mylist类,继承自内置数据类型list(列表)
- 增加一个方法“累乘”product
- def product(self):
- 返回所有数据项的乘积
4. 编写程序,输入两个数,输出它们的商
采用例外处理来处理两种错误,给出用户友好的提示信息
- 除数为0
- 输入了非数值
5. 编写一个推导式,生成包含100以内所有勾股数(i,j,k)的列表
6. 编写一个生成器函数,能够生成斐波那契数列
def fib():
….
for fn in fib():
print (fn)
if fn>1000:
break
「资料来源:Python语言基础与应用-陈斌」