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  #终止循环