只要继承object类就是新式类
不继承object类的都是经典类
在python3 中所有的类都继承object类,都是新式类
在python2 中不继承object的类都是经典类
继承object类的就是新式类
经典类:在py3中不存在,在py2中不主动继承object类
-
在py2 中
-
class A:pass ——> 经典类
-
class B(object):pass ——> 新式类
-
-
在py3 中
-
class A:pass ——> 新式类
-
class B(object):pass ——> 新式类
-
在单继承方面(无论是新式类还是经典类都是一样的)
用的是深度优先方法
寻找某一个方法的顺序是:D-->C-->B-->A
越往父类走,是深度
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()
多继承方面
-
广度优先——>在走到一个点,下一个点既可以从深度走,也可以从广度走的时候,总是先走广度,在走深度
-
在经典类中,都是深度优先,总是在一条路走不通之后在换一条路,走过的点不会在走了
-
在新式类中有 mro() ,可以查看寻找顺序
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() print(D.mro()) # 只有在新式类中有,经典类没有 # 输出 D [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
-
C3算法:
如果是单继承:那么总是按照从子类——>父类的顺序来计算查找顺序。
如果是多继承:需要按照自己本类,父类1的继承顺序,父类2的继承顺序.......
merge的规则(C3):
1、如果一个类出现在从左侧到右所有顺序的最左侧,并且没有在其他位置出现,那么先提出来作为继承顺序的中的一个
2、或 一个类出现在从左侧到右顺序的最左侧, 并没有在其他顺序中出现 ,那么先提出来作为继承顺序的中的一个
3、如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中符合上述条件的类
在多继承中:经典类——>是深度优先
新式类——>是广度优先,遵循C3算法,可以用mro()查看顺序
class A: pass class B(A): pass class C(A): pass class D(B): pass class E(C): pass class F(D, E): pass C3算法 A(O) = [AO] ——>A的继承关系 (O==>object) B(A) = [BAO] ——>B的继承关系 C(A) = [CAO] ——>C的继承关系 D(B) = [DBAO] ——>D的继承关系 E(C) = [ECAO] ——>E的继承关系 F(D,E) = merge(D(B) + E(C)) ——>F的继承关系 继承顺序 = [F] + [DBAO] + [ECAO] ——>自己类加上两个父类的继承顺序 F = [DBAO] + [ECAO] ——>取出左侧第一个F(条件右侧没有F) FD = [BAO] + [ECAO] ——>取出左侧第一个D(条件右侧没有D) FDB = [AO] + [ECAO] ——>左侧第一个A,右侧有A,跳过取右侧第一个E FDBE = [AO] + [CAO] ——>同上取右侧第一个C FDBEC = [AO] + [AO] ——>两边都是相同的取第一个A FDBECA = [O] + [O] ——>同上在取第一个O FDBECAO ——>最终继承顺序
二、父类对子类的约束
抽象类:是一个开发的规范,约束它的所有子类必须实现一些和它同名的方法
列如:支付程序。
-
微信支付 URL链接,告诉你参数什么格式
-
{ ' username ' : ' 用户名 ' , ' money ' : 200 }
-
-
支付宝支付 URL链接,告诉你参数什么格式
-
{ ' uname ' : ' 用户名 ' , ' price' : 200 }
-
方法一:
class Payment: # 这是个抽象类 def pay(self, money): ''' 只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法 ''' raise NotImplementedError('请在类中重写重名pay类方法') # 主动抛异常 class WeChat(Payment): def __init__(self, username): self.username = username def pay(self, money): # pay方法名字不能改变 dic = {'username': self.username, 'money': money} ''' 调用微信支付 url连接 把dic传过去 ''' print(f'{self.username}通过微信充值了{money}') class Alipay(Payment): def __init__(self, username): self.username = username def pay1(self, money): dic = {'uname': self.username, 'price': money} '''' 调用支付宝支付 url连接 把dic传过去 ''' print(f'{self.username}通过支付宝充值了{money}') # 归一化设计:同事或用户使用此类时,直接调用pay函数传参,不用自己创建对象 def pay(username, money, kind): if kind == 'WeChat': obj = WeChat(username) elif kind == 'Alipay': obj = Alipay(username) obj.pay(money) pay('小杨', 200, 'WeChat') # 当支付宝的pay方法名字发生改变时 pay('小杨', 200, 'Alipay') # 输出 小杨通过微信充值了200 报错:NotImplementedError: 请在类中重写重名pay类方法
方法二:实现抽象类的另一种方式,约束力强,依赖abc模块
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # 这是个抽象类 @abstractmethod def pay(self, money): pass class WeChat(Payment): def __init__(self, username): self.username = username def pay(self, money): # pay方法名字不能改变 dic = {'username': self.username, 'money': money} ''' 调用微信支付 url连接 把dic传过去 ''' print(f'{self.username}通过微信充值了{money}') class Alipay(Payment): def __init__(self, username): self.username = username def pay1(self, money): dic = {'uname': self.username, 'price': money} '''' 调用支付宝支付 url连接 把dic传过去 ''' print(f'{self.username}通过支付宝充值了{money}') # 当支付宝的pay名字发生变化的时候 Alipay('xiao') # 这种方法在实例化对象的时候就会报错提示 # 输出 TypeError: Can't instantiate abstract class Alipay with abstract method pay
三、多态
一个类型表现出来的多种状态:
-
同一个对象,多种形态。python默认支持多态
def func(count): # 这里的count可以是str、int、list、dict等等....count就是多态的 print(count) func('abc') func(12345) func([1, 2, 3, 4]) func({'a': 1, 'b': 2}) # 输出 abc 12345 [1, 2, 3, 4] {'a': 1, 'b': 2}
而在Java的情况下:
-
一个参数必须指定类型
-
所以如果想两个类型的对象都可以传,那么必须让着两个继承自一个父类,在指定类型的时候使用父类来指定
-
在java或者c#定义变量或者给函数传值必须定义数据类型,否则就报错。
def func(int a): print('a必须是数学')
-
而类似于python这种弱定义类语言,a可以是任意形态(str,int,object等等)。
def func(a): print('a是什么都可以')
python伪代码实现Java或C的多态
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
鸭子类型
在python中,有一句谚语,你看起来像鸭子,那么你就是鸭子。
对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。
比如:str、tuple、list 都有index方法,这就是互称为鸭子类型
class A: def f1(self): print('in A f1') def f2(self): print('in A f2') class B: def f1(self): print('in A f1') def f2(self): print('in A f2') obj = A() obj.f1() obj.f2() obj2 = B() obj2.f1() obj2.f2() # A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。 # 输出 in A f1 in A f2 in A f1 in A f2
内置的数据结构:
-
{}:——key-value 通过key找v非常快
-
[]:——序列 通过index取值非常快
-
():——元组
-
{1,}:——集合
-
'abc':——字符串
不是python内置的:
-
Queue 队列:先进先出 FIFO (FIRST IN FIRST OUT)
-
put:进
-
get:出
-
-
Stack 栈:后进先出 LIFO (LAST IN FIRST OUT)
-
put:进
-
get:出
-
class My_List: def __init__(self): self.ll = [] def put(self, count): self.ll.append(count) class Stack(My_List): def get(self): return self.ll.pop() class Queue(My_List): def get(self): return self.ll.pop(0) q = Queue() s = Stack() for a in range(10): q.put(a) s.put(a) print('队列放进去的值:', q.ll) print('第一次取出: ', q.get()) print('第二次取出: ', q.get()) print('队列所剩值: ', q.ll) print('------------------------------------') print('栈放进去的值: ', s.ll) print('第一次取出: ', s.get()) print('第二次取出: ', s.get()) print('栈所剩值: ', s.ll) # 输出 队列放进去的值: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 第一次取出: 0 第二次取出: 1 队列所剩值: [2, 3, 4, 5, 6, 7, 8, 9] ------------------------------------ 栈放进去的值: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 第一次取出: 9 第二次取出: 8 栈所剩值: [0, 1, 2, 3, 4, 5, 6, 7]
自定义Pickle,借助pickle模块来完成简化的dump和load
-
pickle dump
-
打开文件
-
把数据dump到文件里
-
-
pickle load
-
打开文件
-
读数据
-
对象 = Mypickle('文件路径')
对象.load() 能拿到这个文件中所有的对象
对象.dump(要写入文件的对象)
import pickle class Mypickle: def __init__(self, path): self.path_ = path def myload(self): with open(self.path_, mode='rb') as f1: while True: try: # 让读取到的数据变成迭代器 yield pickle.load(f1) except EOFError: break def mydump(self, count): with open(self.path_, mode='ab') as f2: pickle.dump(count, f2) # 需要放入文件的数据 ll = [f'第{a}个' for a in range(3)] # 实例化一个对象 obj = Mypickle(r'my_obj') obj.mydump(ll) # 写入文件 obj.myload() # 读取文件的数据 # 可以用__next__一条一条的读,也可以for循环读 a = obj.myload().__next__() print(a) print('------------------------') # for循环读取迭代器内的数据 for a in obj.myload(): print(a) # 输出 ['第0个', '第1个', '第2个'] ------------------------ ['第0个', '第1个', '第2个'] ['第0个', '第1个', '第2个'] ['第0个', '第1个', '第2个']