内置数据结构,面向对象和继承回顾,继承顺序,抽象类,归一化设计,多态,鸭子类型
程序员文章站
2022-03-06 10:29:32
数据结构: 面向对象回顾: 继承回顾: 继承进阶: 继承(进阶的知识点): 多继承的继承顺序问题(项目和源码会用到) 通过继承实现的类的开发规范(工作中会用到) C3算法: 抽象类 父类对子类的约束: 多态: python当中处处是多态,一切皆对象。 什么是多态,借助java理解。 鸭子类型。 总结 ......
数据结构:
1. 内置的数据结构: {} key-value 通过key找v非常快 [] 序列 通过index取值非常快 () 元组 {1,}集合 'sx'字符串 2. 非python内置的数据结构: 【1】 queue队列 : 先进先出 fifo(first in first out) #put #get 【2】 stack栈 : 后进先出 lifo(last in first out) #put #get 【3】 用继承关系完成代码的简化 3.练习: 【1】 队列 class queue: def __init__(self): self.l = [] def put(self,item): self.l.append(item) def get(self): return self.l.pop(0) q = queue() q.put(1) q.put(2) q.put(3) print(q.get())#1 print(q.get())#2 print(q.get())#3 【2】 栈 class stack: def __init__(self): self.l = [] def put(self,item): self.l.append(item) def get(self): return self.l.pop() s1 = stack() s2 = stack() s1.put(1) s2.put('a') s2.put('b') s2.put('c') s1.put(2) s1.put(3) print(s2.get())#c print(s1.get())#3 print(s2.get())#b print(s2.get())#a print(s1.get())#2 s1.put(4) print(s1.get())#4 print(s1.get())#1 【3】用继承关系完成栈和队列代码的简化 # 方法一 class foo: def __init__(self): self.l = [] def put(self, item): self.l.append(item) class queue(foo): def get(self): return self.l.pop(0) class stack(foo): def get(self): return self.l.pop() #方法二: class foo: def __init__(self): self.l = [] def put(self,item): self.l.append(item) def get(self): return self.l.pop() if self.index else self.l.pop(0) class queue(foo): def __init__(self): self.index = 0 foo.__init__(self) class stack(foo): def __init__(self): self.index = 1 foo.__init__(self) #方法三: class foo: def __init__(self): self.l = [] def put(self, item): self.l.append(item) def get(self): return self.l.pop() class queue(foo): def get(self): return self.l.pop(0) class stack(foo):pass 【4】自定义pickle,借助pickle模块来完成简化的dump和load # pickle dump # 打开文件 # 把数据dump到文件里 # pickle load # 打开文件 # 读数据 # 对象 = mypickle('文件路径') # 对象.load() 能拿到这个文件中所有的对象 # 对象.dump(要写入文件的对象) ##1: import pickle class mypickle: def __init__(self,path): self.file = path def dump(self,obj): with open(self.file, 'ab') as f: pickle.dump(obj, f) def load(self): with open(self.file,'rb') as f: while true: try: yield pickle.load(f) except eoferror: break pic = mypickle('pickle_file') s1 = stack() #利用上面的栈 s1.put('aaa') s1.put(123) s1.put(456) pic.dump(s1) s2 = stack() s2.put('bbb') s2.put(888) s2.put(999) pic.dump(s2) for i in pic.load(): print(i.l) # ['aaa', 123, 456] # ['bbb', 888, 999] ##2: load(self)与上面略有不同 import pickle class mypickle: def __init__(self,path): self.file = path def dump(self,obj): with open(self.file, 'ab') as f: pickle.dump(obj, f) def load(self): l = [] with open(self.file,'rb') as f: while true: try: l.append(pickle.load(f)) except eoferror: break return l pic = mypickle('pickle_file') pic.dump('aaa') pic.dump('bbb') ret = pic.load() print(ret) #['aaa', 'bbb'] 【5】自定义json,借助json模块来完成简化的dump和load。仿照这个类写一个myjson(可以dump多次,load多次) #自: #1:通过dumps 和 loads 实现 import json class json: def __init__(self,path): self.path = path def my_dump(self,data): with open(self.path,mode='a') as f: f.write(json.dumps(data) + '\n') def my_load(self): with open(self.path,mode= 'r') as f: for i in f: print(json.loads(i)) a = json('info') a.my_dump('aa') a.my_dump('bb') a.my_dump('cc') a.my_load() # aa # bb # cc #2:通过dump 和 load 实现 import json import os class json: def __init__(self,path): self.file = path def dump(self,obj): if os.path.getsize(self.file): f1 = open(self.file,encoding='utf-8') res = json.load(f1) f1.close() f2 = open(self.file, encoding='utf-8',mode='w') f2.write('') f2.close() f3 = open(self.file, encoding='utf-8', mode='w') msg = res + obj json.dump(msg, f3) f3.close() else: res = '' f3 = open(self.file,encoding='utf-8',mode='w') msg = res + obj json.dump(msg, f3) f3.close() def load(self): with open(self.file,encoding='utf-8') as f: return json.load(f) a = json('info') a.dump('aa') a.dump('bb') a.dump('cc') print(a.load())
面向对象回顾:
1. 类:所有的变量和函数的地址都存储在类的命名空间里 class 类名: 静态变量 = '值' def 函数(self): '函数体的内容' 2. 对象: 对象 = 类名 + () 3. 怎么用: 【1】 类能做什么用? 1.实例化对象 2.操作静态变量 【2】 什么时候是对类中的变量赋值,或者去使用类中的变量? 类名.名字 = '值' print(类名.名字) print(对象名.名字) # 如果对象本身没有这个名字 【3】 什么时候是对对象中的变量赋值? 对象.名字 的时候 self.名字 的时候 【4】假设你希望一个类的多个对象之间 的某个属性 是各自的属性,而不是共同的属性,这个时候我们要把变量存储在对象的命名空间中,不能建立静态变量,建立静态变量是所有的对象共同使用一个变量。 【5】所有的对象调用方法 就看这个对象是哪一个类的对象。不要担心所有的类的方法都是一样的名字,并不影响的。 对象.方法 看 . 之前的对象到底属于哪个类。 [1,2,3].append(4) 对象[1,2,3]是列表类型 4.练习: #1: class a: role = [] def __init__(self): self.l = [] def append(self,obj): self.l.append(obj) def pop(self,index=-1): self.l.pop(index) print(a.role)#[] a = a()#之前的a 与 a() 之间的联系会断开 a = a()#a重复赋值,永远等于最后赋值的那一次 #2: class b: def append(self): print('bbbb') class c: def append(self): print('cccc') b = b() b.obj = [] b.obj.append(1) print(b.obj)#[1] b = b() b.append()#bbbb c = b() c.append()#bbbb d = c() d.append()#cccc #3: class b: def append(self,value): self.l.append(value) class c: def append(self,value): print('ccc') b = b() b.l = [] b.append(10) print(b.l)#[10] #4: class queue: def __init__(self): self.lst = [] def append(self,value): pass def pop(self): pass q = queue() q.lst.append(10) q.append(5) print(q.lst)#[10] q.pop() print(q.lst)#[10]
继承回顾:
1.继承 是通过继承来解决代码重用的问题 2.怎么继承 class a: def func(self):print('a') class b(a): def func(self):print('b') a是父类,b是子类 写代码的时候,是先有的父类还是先有的子类? 【1】在加载代码的过程中 需要先加载父类 所以父类写在前面。 【2】从思考的角度出发 总是先把子类都写完 发现重复的代码 再把重复的代码放到父类中。 3.练习: #1: class a: def func(self):print('a') class b(a): def func(self):print('b') b = b() b.func() # b 自己有不用父类的 #2: class a: def func(self):print('a') class b(a):pass b = b() b.func() # a 自己没有用父类的 #3: class a: def func(self):print('a') class b(a): def func(self): a.func(self) print('b') b = b() b.func() # 先执行b.func,调用了a.func打印a,然后回到b.func打印b # a # b #4: class a: def func(self):print('a') class b(a): def func(self): print('b') a.func(self) b = b() b.func() # b # a #5: class a: lst = [] def func(self): self.lst.append(1) class b(a): lst = [] def func(self): self.lst.append(2) b = b() b.func() print(a.lst) # [] print(b.lst) # [2] #6: class a: lst = [] def func(self): self.lst.append(1) class b(a): def func(self): self.lst.append(2) b = b() b.func() print(a.lst) # [2] print(b.lst) # [2] #7: class a: lst = [] def __init__(self): self.lst = [] def func(self): self.lst.append(1) class b(a): def __init__(self): self.lst= [] def func(self): self.lst.append(2) b = b() b.func() print(b.lst) #[2] print(a.lst) # [] print(b.lst) # []
继承进阶:
- 继承(进阶的知识点):
- 多继承的继承顺序问题(项目和源码会用到)
- 通过继承实现的类的开发规范(工作中会用到)
1.【记】 【1】只要继承object类就是新式类 【2】不继承object类的都是经典类 【3】在python3中 所有的类都继承object类,都是新式类。 【4】在python2中 不继承object的类都是经典类,继承object类的就是新式类了。 【5】经典类 :在py3中不存在,在py2中是不主动继承object的类 【6】举例: 在py2中 class a:pass # 经典类 class b(object):pass # 新式类 在py3中 class a:pass # 新式类 class b(object):pass # 新式类 2.在单继承方面(无论是新式类还是经典类都是一样的) class a: def func(self):pass class b(a): def func(self):pass class c(b): def func(self):pass class d(c): def func(self):pass d = d() 【1】 寻找某一个方法的顺序:d-> c-> b-> a 【2】 越往父类走,是深度 3.多继承的继承顺序问题: 【1】在新式类中,在走到一个点,下一个点既可以从深度走,也可以从广度走的时候,总是先走广度,再走深度,广度优先。 - 广度优先遵循c3算法,要会用mro,会查看顺序 【2】在经典类中,都是深度优先,总是在一条路走不通之后再换一条路(总是往深了找,一直找到头,没有再找下一条线),走过的点不会再走了。 - 深度优先要会看,自己能写出顺序来 【3】 经典类 - 深度优先 新式类 - 广度优先 经典类没有mro,但新式类有 #1: class a: def func(self): print('a') class b(a): def func(self): print('b') class c(a): def func(self): print('c') class d(b,c): def func(self): print('d') d = d() d.func()#d #2: class a: def func(self): print('a') class b(a): def func(self): print('b') class c(a): def func(self): print('c') class d(b,c): pass d = d() d.func()#b #3: class a: def func(self): print('a') class b(a): pass class c(a): def func(self): print('c') class d(b,c): pass d = d() d.func()#c #4: class a: def func(self): print('a') class b(a): pass class c(a): pass class d(b,c): pass d = d() d.func()#a #5: class a: pass class b(a): pass class c(a): pass class d(b,c): pass d = d() d.func()#报错 attributeerror: 'd' object has no attribute 'func' #6: 用mro查看顺序:mro能够查看c3的结果 class a: pass class b(a): pass class c(a): pass class d(b,c): pass print(d.mro()) # 只在新式类中有,经典类没有的 #[<class '__main__.d'>, <class '__main__.b'>, <class '__main__.c'>, <class '__main__.a'>, <class 'object'>]
c3算法:
c3算法 #计算继承按照加载顺序,从上往下,从左到右去算,父类在上,子类在下 a(o) = [ao] #先算a的继承顺序,a继承o。单继承上顺序就是 ao b(a) = [bao] #b继承a,b的继承顺序也是单继承 bao c(a) = [cao] #c继承a,c的继承顺序也是单继承 cao d(b) = [dbao] #d继承b,d的继承顺序也是单继承 dbao e(c) = [ecao] #e继承c,e的继承顺序也是单继承 ecao # f是多继承,采用c3算法 f(d,e) = merge(d(b) + e(c)) = [f] + [dbao] + [ecao] #提取规则:如果从左到右出现了某一个类,这个类是每一个节点中的第一个,比如[fdbao] [fecao]f是第一个 或者 从左到右出现了一个类,并且它没有出现在后面的节点中,就把它提取出来。 f = [dbao] + [ecao] fd = [bao] + [ecao] fdb = [ao] + [ecao] fdbe = [ao] + [cao] fdbec = [ao] + [ao] fdbeca = [o] + [o] fdbecao # 算法的内容 如果是单继承 那么总是按照从子类->父类的顺序来计算查找顺序 如果是多继承 需要按照自己本类,+ 父类1的继承顺序,+ 父类2的继承顺序,+...... merge的规则 : 如果一个类出现在从左到右所有顺序的最左侧,并且没有在其他位置出现,那么先提出来作为继承顺序中的一个。 如果从左到右出现了某一个类,这个类是每一个节点中的第一个那么先提出来作为继承顺序中的一个。 如果从左到右第一个顺序中的第一个类出现在后面 但 不是第一个,那么不能提取,顺序向后继续找其他顺序中符合上述条件的类。
抽象类---父类对子类的约束:
普通的类 :之前写的类都是普通类,如class a 抽象类:是一个开发的规范 约束它的所有子类必须实现一些和它同名的方法。 举例: 支付程序: 微信支付 url连接,告诉你参数什么格式 {'username':'用户名','money':200} 支付宝支付 url连接,告诉你参数什么格式 {'uname':'用户名','price':200} 苹果支付 {'name': '用户名', 'number': 200} #引入: 归一化设计 class alipay: def __init__(self,name): self.name = name def pay(self,money): dic = {'uname':self.name,'price':money} # 想办法调用支付宝支付 url连接 把dic传过去 print('%s通过支付宝支付%s钱成功'%(self.name,money)) class wechat: def __init__(self,name): self.name = name def pay(self,money): dic = {'username':self.name,'money':money} # 想办法调用微信支付 url连接 把dic传过去 print('%s通过微信支付%s钱成功'%(self.name,money)) class apple: def __init__(self,name): self.name = name def pay(self,money): dic = {'name': self.name, 'number': money} # 想办法调用苹果支付 url连接 把dic传过去 print('%s通过苹果支付%s钱成功' % (self.name, money)) def pay(name,price,kind): #归一化设计,写一个归一化函数,帮助自己完成实例化对象。并且调用支付这个功能,等一系列操作。 if kind == 'wechat': obj = wechat(name) elif kind == 'alipay': obj = alipay(name) elif kind == 'apple': obj = apple(name) obj.pay(price)#统一调用同名的pay方法。每个支付方式类的支付方法都必须叫pay pay('alex',400,'wechat') #之前的代码都是自己写的,此处代码是供别人调用的,别人只需要调用你实现好的函数。他不用关心到底是哪个类的对象,只需要选择对应的支付方式,归一化函数替他完成实例化和调用。 pay('alex',400,'alipay') pay('alex',400,'apple') # alex通过微信支付400钱成功 # alex通过支付宝支付400钱成功 # alex通过苹果支付400钱成功 #做一个规范,要求必须实现一个功能叫做pay,如果不实现就报错。 1.实现抽象类方式1:raise主动抛异常 raise + 内置错误 由父类定制规范,所有继承它的子类,必须按照这个要求去实现这个规范。规范类,不是用来做具体工作的,只是用来为接下来所有的子类提供一个规范。只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法,完成相同的功能。 此处payment类就是一个抽象类,就是一个开发的规范,约束它的所有子类必须实现一些和它同名的方法。 class payment: #规范类,不是用来做具体工作的,只是用来为接下来所有的子类提供一个规范。 def pay(self,money): #self后面为规定的参数,下面的子类中的pay方法有什么参数,此处必须按同样的方式写 '''只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法''' raise notimplementederror('请在子类中重写同名pay方法') #raise主动抛异常 raise + 内置错误,没有实现的异常 ()为提示信息 ,进行规范的报错,报错是给程序员看的 class alipay(payment): #继承了payment def __init__(self,name): self.name = name def pay(self,money): dic = {'uname':self.name,'price':money} # 想办法调用支付宝支付 url连接 把dic传过去 print('%s通过支付宝支付%s钱成功'%(self.name,money)) class wechat(payment): #继承了payment def __init__(self,name): self.name = name def pay(self,money): dic = {'username':self.name,'money':money} # 想办法调用微信支付 url连接 把dic传过去 print('%s通过微信支付%s钱成功'%(self.name,money)) class apple(payment): def __init__(self,name): self.name = name def pay(self,money): # def fuqian(self,money): 会报错 dic = {'name': self.name, 'number': money} # 想办法调用苹果支付 url连接 把dic传过去 print('%s通过苹果支付%s钱成功' % (self.name, money)) aw = wechat('alex') aw.pay(400) aa = alipay('alex') aa.pay(400) bb = apple('wusir') bb.pay(400) #调用pay方法时,假如子类不是用的pay作为方法名,子类中找不到pay就去父类找,父类找到pay就直接给你主动抛出异常,告诉你在子类中必须实现同名的方法。只有在调用pay方法的时候才可能报错,实例化时不会报错。 # alex通过微信支付400钱成功 # alex通过支付宝支付400钱成功 # wusir通过苹果支付400钱成功 2:实现抽象类的另一种方式:约束力强,但依赖abc模块 #【记】 from abc import abcmeta,abstractmethod class payment(metaclass=abcmeta): @abstractmethod #导入的装饰器 def pay(self,money): #假如子类中没有同名的pay方法,连实例化都做不了,会报错,告诉你没有实现抽象类里的pay方法。测试时非常容易发现存在的问题 pass class alipay(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'uname':self.name,'price':money} # 想办法调用支付宝支付 url连接 把dic传过去 print('%s通过支付宝支付%s钱成功'%(self.name,money)) class wechat(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'username':self.name,'money':money} # 想办法调用微信支付 url连接 把dic传过去 print('%s通过微信支付%s钱成功'%(self.name,money)) class apple(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'name': self.name, 'number': money} # 想办法调用苹果支付 url连接 把dic传过去 print('%s通过苹果支付%s钱成功' % (self.name, money)) aw = wechat('alex') aw.pay(400) aa = alipay('alex') aa.pay(400) bb = apple('wusir') bb.pay(400) # alex通过微信支付400钱成功 # alex通过支付宝支付400钱成功 # wusir通过苹果支付400钱成功
多态:
- python当中处处是多态,一切皆对象。
- 什么是多态,借助java理解。
- 鸭子类型。
1.多态:python中处处是多态,不需要依赖继承来实现多态。在java中通过继承实现多态,通过java代码理解什么是多态。 【记】【面试考】 #1: def add(int a,int b): # 在java 中每一个参数在传值之前就确定了类型是什么。但在python中,传参时不检测数据类型 return a+b print(add(1,'asuhjdhdgl')) #这样在传参时就会报错 #2: class payment:pass #payment什么都不实现 class wechat(payment): #继承payment def __init__(self,name): self.name = name def pay(self,money): dic = {'username':self.name,'money':money} # 想办法调用微信支付 url连接 把dic传过去 print('%s通过微信支付%s钱成功'%(self.name,money)) class apple(payment): #继承payment def __init__(self,name): self.name = name def pay(self,money): dic = {'name': self.name, 'number': money} # 想办法调用苹果支付 url连接 把dic传过去 print('%s通过苹果支付%s钱成功' % (self.name, money)) 归一化设计 java 归一化设计存在问题: def pay(apple a, int b): a.pay(b) obj = apple('alex') pay(obj,400) def pay(apple a, int b): a.pay(b) obj = wechat('alex') pay(obj,400) 归一化设计 在java中通过继承实现多态 def pay(payment a, int b): #某个位置传值的时候如果可能出现两个类型apple,wechat去调用同一个方法,那么搞一个两个类型的父类出来,让apple,wechat都继承payment a.pay(b) #形参某个位置写的是父类的类名,传参的时候传的是子类的对象 obj = apple('alex') pay(obj,400) obj = wechat('alex') pay(obj,400) 【多态是指一个类型表现出来的多种状态,上面:支付 表现出的 微信支付 和 苹果支付 这两种状态。(一个父类表现出多个子类的几种状态) 在java情况下: 一个参数必须指定类型, 所以如果想让两个类型的对象都可以传,那么必须让这两个类继承自一个父类,在指定类型的时候使用父类来指定。】 2. 鸭子类型: #例1: len()是如何计数的?原理参考视频。在存储的时候就知道列表中有多少个值了,而不是在调用len(l)的时候才去计算 在 pyhton 中 class list: def __len__(self):pass class dict: def __len__(self): pass class set: def __len__(self): pass class tuple: def __len__(self): pass class str: def __len__(self): pass def len(obj): return obj.__len__() print(dir(list)) #但凡可以使用len()计算长度的,在其内部都包含__len__方法 print(dir(str)) 所有实现了__len__方法的类,在调用len函数的时候,对象obj都说是len()的鸭子类型。len()的鸭子类型:看其有没有实现一个__len__方法 #例2: # 迭代器协议:__iter__和 __next__ 方法的是迭代器 class range: def __iter__(self): pass def __next__(self): pass print(dir(range)) #range不用继承迭代器类,只要内部含有 '__iter__ ' 和 '__next__' 方法,它看起来就是一个迭代器。range就是一个迭代器类型的鸭子类型 #通过明确的继承关系,可知 range 是 object 类。
总结:
1.类的种类 【1】新式类 : 继承object,存在在py2,py3(py3中都是新式类,py2里主动继承object的才是新式类) 【2】经典类 : 只在py2中,不继承object默认是经典类 2. 继承顺序 【1】 深度优先 : 经典类 【2】 广度优先 : 新式类 - 查看广度优先的顺序 : 类名.mro() - 遵循的算法 :c3 3.抽象类 【1】为什么要用抽象类 : 为了规范 子类 必须实现和 父类 的 同名方法。 【2】 抽象类用到的格式(至少会写一种,两种见到了都要认识) (1)不需要模块的: class 父类: def 子类必须实现的方法名(self,参数们): raise notimplementederror('提示信息') class 子类(父类): def 父类要求实现的方法(self,参数们): print('''code''') (2)需要模块的: from abc import abcmeta,abstractmethod class 父类(metaclass = abcmeta): @abstractmethod def 子类必须实现的方法名(self,参数们):pass class 子类(父类): def 父类要求实现的方法(self,参数们): print('''code''') 【3】归一化设计: class a: def 同名功能(self):pass class b: def 同名功能(self):pass def 函数名(obj): obj.同名功能() 4.多态 【1】 什么是多态 : 一个类表现出的多种形态,实际上是通过“继承”来完成的。主要是指一个子类的对象也是父类类型的。 举一反三: # 如果狗类继承动物类,猫类也继承动物类, # 那么我们就说猫的对象也是动物类型的, # 狗的对象也是动物类型的 # 在这一个例子里,动物这个类型表现出了猫和狗的形态,就是多态 #自: # 如果足球类继承球类,篮球类也继承球类, # 那么我们就说足球的对象也是球类类型的, # 篮球的对象也是球类类型的 # 在这一个例子里,球类这个类型表现出了足球和篮球的形态,就是多态 【2】 java中的多态是什么样? def eat(动物类型 猫的对象/狗的对象,str 食物): print('动物类型保证了猫和狗的对象都可以被传递进来') 【3】 python中的多态是什么样 : 处处是多态 【4】 鸭子类型: 在python中,一个类可以是很多类的鸭子类型。比如:元组类的数据是可哈希的,又不依靠继承哈希类来判定是不是可哈希类型,而是内部含有__hash__方法,所以元组类是可哈希类型的鸭子类型。 可迭代对象:str bytes list tuple dict set range,他们都是可迭代对象,但是又不依靠继承继承可迭代类型来判定是不是可迭代类型,而是内部含有__iter__方法,就说是可迭代的。 在python中,一个类是不是属于某一个类型, 不仅仅可以通过继承来完成 【 子类继承父类,我们说子类是父类类型的(猫类继承动物,我们说猫也是动物) 】 还可以不是继承,如果这个类满足了某些类型的特征条件,我们就说它长得像这个类型,那么它就是这个类型的鸭子类型。 (自:a是b类型,但是a不是通过继承来确定是不是b类型,而是通过其他方式判定属于这个类型,我们就说a是b的鸭子类型。) 在继承关系中:python中某个对象aa是通过bb实例化出来的,就说aa是bb类型的。 通过继承说对象aa是某个类型的,对象aa要么是这个类的实例化,要么是这个类的子类的实例化。 5. 就记住两件事儿【重点】: -所有的类都必须继承object。如:class a(object):pass -如果见到了抽象类的写法,一定要知道要在子类中实现同名方法
上一篇: div仿td标签属性