一、多态与多态性
多态:
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有talk方法,
于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用。
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.talk()
综上我们得知,多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,
可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
鸭子类型:
但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,
这正是Python崇尚的“鸭子类型”(duck typing):“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度。
二、面向对象高级
内置方法:
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,
会在满足某种条件时自动触发,我们以常用的__str__和__del__为例来简单介绍它们的使用。
__str__:__str__方法会在对象被打印时自动触发,print功能打印的就是它的返回值,我们通常基于方法来定制对象的打印信息,该方法必须返回字符串类型
__del__:__del__会在对象被删除时自动触发。由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,
完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,
关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作。
三、反射
在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)
基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行。
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Egon Lin')
# hasattr(object,'name')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
# getattr(object, 'name', default=None)
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
# setattr(x, 'y', v)
setattr(t,'age',18) # 等同于t.age=18
# delattr(x, 'y')
delattr(t,'age') # 等同于del t.age
>>> class FtpServer:
... def serve_forever(self):
... while True:
... inp=input('input your cmd>>: ').strip()
... cmd,file=inp.split()
... if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
... func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
... func(file)
... def get(self,file):
... print('Downloading %s...' %file)
... def put(self,file):
... print('Uploading %s...' %file)
...
>>> server=FtpServer()
>>> server.serve_forever()
input your cmd>>: get a.txt
Downloading a.txt...
input your cmd>>: put a.txt
Uploading a.txt...
四、异常
异常是程序发生错误的信号。程序一旦出现错误,便会产生一个异常,若程序中没有处理它,就会抛出该异常,程序的运行也随之终止。
1)、异常的追踪信息
2)、异常类型
3)、异常的值
而错误分成两种,一种是语法上的错误SyntaxError,这种错误应该在程序运行前就修改正确。如
>>> if
File "<stdin>", line 1
if
^
SyntaxError: invalid syntax
另一类就是逻辑错误,常见的逻辑错误。如
# TypeError:数字类型无法与字符串类型相加
1+’2’
# ValueError:当字符串包含有非数字的值时,无法转成int类型
num=input(">>: ") #输入hello
int(num)
# NameError:引用了一个不存在的名字x
x
# IndexError:索引超出列表的限制
l=['egon','aa']
l[3]
# KeyError:引用了一个不存在的key
dic={'name':'egon'}
dic['age']
# AttributeError:引用的属性不存在
class Foo:
pass
Foo.x
# ZeroDivisionError:除数不能为0
1/0
异常处理:
为了保证程序的容错性与可靠性,即在遇到错误时有相应的处理机制不会任由程序崩溃掉,我们需要对异常进行处理,处理的基本形式为:
try:
被检测的代码块
except 异常类型:
检测到异常,就执行这个位置的逻辑
例①:
try:
print('start...')
print(x) # 引用了一个不存在的名字,触发异常NameError
print('end...')
except NameError as e: # as语法将异常类型的值赋值给变量e,这样我们通过打印e便可以知道错误的原因
print('异常值为:%s' %e)
print('run other code...')
#执行结果为
start...
异常值为:name 'x' is not defined
run other code...
其他:
try:
被检测的代码块
except NameError:
触发NameError时对应的处理逻辑
except IndexError:
触发IndexError时对应的处理逻辑
except KeyError:
触发KeyError时对应的处理逻辑
例②:
try:
被检测的代码块
except (NameError,IndexError,TypeError):
触发NameError或IndexError或TypeError时对应的处理逻辑
如果我们想捕获所有异常并用一种逻辑处理,Python提供了一个万能异常类型Exception:
例③:
try:
被检测的代码块
except NameError:
触发NameError时对应的处理逻辑
except IndexError:
触发IndexError时对应的处理逻辑
except Exception:
其他类型的异常统一用此处的逻辑处理
在多分支except之后还可以跟一个else(else必须跟在except之后,不能单独存在),
只有在被检测的代码块没有触发任何异常的情况下才会执行else的子代码块。
例④:
try:
被检测的代码块
except 异常类型1:
pass
except 异常类型2:
pass
......
else:
没有异常发生时执行的代码块
此外try还可以与finally连用,从语法上讲finally必须放到else之后,但可以使用try-except-finally的形式,也可以直接使用try-finally的形式。
无论被检测的代码块是否触发异常,都会执行finally的子代码块,因此通常在finally的子代码块做一些回收资源的操作,比如关闭打开的文件、关闭数据库连接等。
例⑤:
try:
被检测的代码块
except 异常类型1:
pass
except 异常类型2:
pass
......
else:
没有异常发生时执行的代码块
finally:
无论有无异常发生都会执行的代码块
在不符合Python解释器的语法或逻辑规则时,是由Python解释器主动触发的各种类型的异常,而对于违反程序员自定制的各类规则,
则需要由程序员自己来明确地触发异常,这就用到了raise语句,raise后必须是一个异常的类或者是异常的实例。
最后,Python还提供了一个断言语句assert expression,断定表达式expression成立,否则触发异常AssertionError,与raise-if-not的语义相同,如下
age='18'
# 若表达式isinstance(age,int)返回值为False则触发异常AssertionError
assert isinstance(age,int)
# 等同于
if not isinstance(age,int):
raise AssertionError
在了解了异常处理机制后,本着提高程序容错性和可靠性的目的,读者可能会错误地认为应该尽可能多地为程序加上try...except...,
这其是在过度消费程序的可读性,因为try...except本来就是你附加给程序的一种额外的逻辑,与你的主要工作是没有多大关系的。
如果错误发生的条件是“可预知的”,我们应该用if来进行”预防”。
如果错误发生的条件“不可预知”,即异常一定会触发,那么我们才应该使用try...except语句来处理。
例如我们编写一个下载网页内容的功能,网络发生延迟之类的异常是很正常的事,而我们根本无法预知在满足什么条件的情况下才会出现延迟,因而只能用异常处理机制了。