13 多态
1.多态
其他语言中所谓多态,指的是一个方法多种实现,在继承前提下,父类规定了子类的接口,使得可以相同的方式调用子类的方法,但会获得不同的功能。
Python崇尚的是“鸭子类型”(Duck typing),这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”。“鸭子类型”中不关心对象的类型,只关心对象的行为。
鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
class Duck:
def walk(self):
print('鸭子走路')
def swim(self):
print("鸭子游泳")
class Cat(Animal):
def walk(self):
print('喵喵走路')
def swim(self):
print("喵喵游泳")
def show(obj):
obj.walk() #只要传入的对象有walk和swim方法就认为是鸭子
obj.swim()
duck = Duck()
cat = Cat()
show(dog)
show(cat)
2.其它
2.1 类的信息
__name__ 通过类名访问,得到的结果为类名字符串
__dict__ 通过类名访问,获取类的信息,包括类方法,静态方法,成员方法,以字典的形式返回
通过对象访问,获取的是该对象的属性和值,以字典的形式返回
__bases__ 通过类名访问,查看所有的父类 【基类】
__module__ 类所在的模块
__mro__ 类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
2.2 对象信息
class Dog:
def init(self):
self.name = 'tom'
self.__age = 3
d1 = Dog()
print(dir(d1)) #对象的信息
print(d1.__class__) #对象的类名
print(d1.__dict__) #对象属性字典
print(d1.__module__) #对象的模块名
2.3 常用函数
内建函数 | 说明 |
issubclass(sub,sup) | 如果sub是sup的子类,返回True,否则返回False;sub和sup必须是类 |
isinstance(obj,class) | 如果obj是class的对象或子类对象,返回True,否则返回False;obj可以对象也可以是类,但class必须是类 |
hasattr(object,name) | 判断对象是否具有指定属性(name),有返回True,否则返回False;属性必须是公有的才能判断 |
getattr(object, name[, default]) | 获取object对象的属性值 |
setattr(object, name, value) | 设置对象的属性值,属性必须存在 |
super(obj,self) | 调用父类(超类)的一个方法。 |
dir(obj/class) | 显示类或对象属性、方法等详细信息 |
callable(object) | 判断一个对象是否可调用 |
3.对象的持久化
数据持久化就是将内存中的对象转换为存储模型,以及将存储模型转换为内存中的对象的统称. 对象可以是任何数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等
Python的数据持久化操作主要是六类:普通文件、DBM文件、Pickle对象存储、shelve对象存储、对象数据库存储、关系数据库存储。
3.1 pickle
pickle可以将所有python支持的原生类型:布尔值,整数,浮点数,复数,字符串,字节,None。以及由任何原生类型组成的列表,元组,字典和集合;函数,类,类的实例保存到文件
- dump 将对象保存到文件
pickle.dump(obj, file[, protocol])
参数的含义分别为:
obj: 要持久化保存的对象;
file: 一个拥有 write() 方法的对象,并且这个 write() 方法能接收一个字符串作为参数。这个对象可以是一个以写模式打开的文件对象或者一个 StringIO 对象,或者其他自定义的满足条件的对象。
protocol: 这是一个可选的参数,默认为 0 ,如果设置为 1 或 True,则以高压缩的二进制格式保存持久化后的对象,否则以ASCII格式保存。
- load 从文件中读取数据,还原成对象
pickle.load(file)
只有一个参数 file ,对应于上面 dump 方法中的 file 参数。这个 file 必须是一个拥有一个能接收一个整数为参数的 read() 方法以及一个不接收任何参数的 readline() 方法,并且这两个方法的返回值都应该是字符串。这可以是一个打开为读的文件对象、StringIO 对象或其他任何满足条件的对象。
import pickle
tmp = [10,20,30]
# 写文件
#必须以二进制方式打开
with open("test1.data",'wb') as fp:
pickle.dump(tmp,fp)
# 读文件
#必须以二进制方式打开
with open('test1.data','rb') as fp:
t2 = pickle.load(fp)
print(t2)
3.2 dbm
在一些python小型应用程序中,不需要关系型数据库时,可以方便的用持久字典来存储名称/值对,它与python的字典非常类似,主要区别在于数据是在磁盘读取和写入的。另一个区别在于dbm的键和值必须是字符串类型。
dbm.open(file, flag=‘r’, mode=0o666)
参数:
file 数据库文件名
flag 打开方式
值 | 含义 |
‘r’ | 打开现有数据库以进行只读(默认) |
‘w’ | 打开现有数据库进行读写 |
‘c’ | 打开用于读取和写入的数据库,如果不存在则创建它 |
‘n’ | 始终创建一个新的空数据库,打开进行读取和写入 |
import dbm
#写入数据库
# with dbm.open('test4','c') as db:
# db['name'] = b"tom"
# db['gender'] = '男'.encode('utf-8')
#读取数据库内容
with dbm.open('test4') as db:
for key in db:
print(db[key].decode('utf-8'))
3.3 shelve
shelve是一个持久的,类似字典的对象。与dbm数据库的区别是,值(而不是键!)可以是基本上任意的Python对象 ,可以处理的任何东西。这包括大多数类实例,递归数据类型和包含大量共享子对象的对象。键是普通字符串。
shelve.open(filename, flag=‘c’, protocol=None, writeback=False)
参数:
filename 数据库文件名
flag 打开方式,同dbm相同
protocol 同dbm
wirteback 写回,如果修改了对象是否写回到数据库文件
import shelve
#写入数据
# with shelve.open('test5',flag='c') as db:
# #直接把db当做字典操作就可以了
# db['a'] = [10,20]
# db['b'] = 30
# db['c'] = 'hello'
#读取
with shelve.open('test5') as db:
for key in db:
print(key,db[key])
#修改
# with shelve.open('test5',writeback=True) as db:
# db['a'].append(30)
# del db['b']
4.生成器
如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
- 延迟计算、惰性求值
- 节省内存,高效
- 缺点:无法随机存取
生成器(Generator) 生成器会产生一个对象,而不是一个列表
4.1 yield表达式
#通过函数和yield关键字生成
#使用了 yield 的函数被称为生成器(generator)
#yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
def test(n):
for i in range(1,n + 1):
yield i
#print(i)
#得到生成器
result = test(10)
print(result)
#生成器只能遍历一次, 所以下面不会有任何输出
for x in result:
print(x)
#可以这样遍历
for x in test(10):
print(x)
#一般不通过这种方式遍历
print(next(result))
4.2生成器表达式
生成器【Generator】生成器会产生一个可迭代对象,而不是一个列表。生成器表达式很类似列表生成式:
(表达式 for var in 可迭代对象)
#将列表生成式中的[]替换成()
ge = (x for x in range(1,6))
print(ge,type(ge))
#生成器需要通过next()方法获取数据,调用一次则返回一个数据
print(next(ge))
print(next(ge))
print(next(ge))
print(next(ge))
print(next(ge))
#注意:如果通过next函数获取生成器中的数据,当数据取完之后,
#将不能再调用next函数,否则出现StopIteration
#print(next(ge)) #StopIteration
#生成器主要通过for-in的方式进行遍历
for x in ge:
print(x,end=' ')
5.迭代器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSHXYFTY-1599811524820)(relationships.png)]
5.1 迭代对象
可以直接作用于for-in循环的数据类型都被称为可迭代对象(Iterable),可以使用isinstance()判断一个对象是否是可迭代对象,可以直接作用于for-in循环的数据类型:
- 数据结构:list、set、tuple、dict、string
- generator【生成器】【(),函数结合yield】
#引入 from collection import Iterable
from collections import Iterable
print(isinstance("",Iterable))#True
print(isinstance({},Iterable))#True
print(isinstance((),Iterable))#True
print(isinstance(1,Iterable))#False
5.2 迭代器
迭代器不但可以作用于for-in循环,还可以使用next()函数将其中的元素获取出来,当获取完最后一个元素之后,当再次调用next方法,则会出现StopIteration错误,表示无法继续返回一个值。可以使用isinstance()判断一个对象是否是迭代器。迭代器的类型是Iterator
from collections import Iterator,Iterable
print(isinstance([],Iterator)) #False
print(isinstance((),Iterator)) #False
print(isinstance({},Iterator)) #False
print(isinstance("",Iterator)) #False
print(isinstance((x for x in range(0,6)),Iterator)) #True 生成器是迭代器
print(isinstance((x for x in range(0,6)),Iterable)) #True 生成器也是迭代对象
结论:list、set、tuple、dict、string是可迭代对象,但是,不是迭代器,只有生成器才是迭代器
5.3 将迭代对象转换为迭代器
迭代器一定是可迭代对象,但是,可迭代对象不一定是迭代器
iter():将可迭代对象转化为迭代器【主要针对list、set、tuple、dict、string】
print(isinstance(iter([]),Iterator)) #True
print(isinstance(iter(()),Iterator)) #True
print(isinstance(iter({}),Iterator)) #True
print(isinstance(iter(""),Iterator)) #True
l2 = iter(l1) #将列表转换为迭代器
print(next(l2)) #使用next获取迭代器中的元素
while True:
try:
print(next(l2)) #可能出问题的代码放到try块中
except StopIteration: #捕获异常
break #终止循环