抽象类:
抽象类是包含抽象方法的类,该类不能被实例化,而抽象方法不包含任何可实现的代码(常用pass语句),只能在其子类中实现抽象函数的代码(一般被子类继承,通过子类实例化后重写方法。
子类一定要实现重写抽象类的所有抽象方法,否则这个子类就是一个抽象类,不能被实例化。
python 本身中不存在抽象类、接口的概念,要实现这种功能需要abc.py这个类库。具体方式:在定义抽象类前需要从类库abc 导入 ABCmeta类(即Metaclass for defining ABstract BaseClasses, 抽象基类的元类)和abstractmethod类。
定义抽象类的步骤:
1、在定义抽象类时需要在类定义中加入如下代码: metaclass=abc.ABCMeta , 即指定该类的元类是 ABCmeta , 所谓元类就是创建类的类。
2、在定义抽象方法时需要在前面加入如下代码: @abstractmethod ; 因为抽象方法不包含任何可实现的代码,因此函数体通常使用 pass 。
import abc from abc import abstractmethod class Test(metaclass=abc.ABCMeta): def __init__(self): print('做点什么') @abstractmethod def do(self): pass
接口:
接口是一种特殊的类。当抽象类中所有的方法都是抽象方法时,或者一个普通类所有的方法都没有实现逻辑,我们可以把这个类当成一个接口 Interface。
Python中并没有 Interface 这个关键字来定义一个接口,其实就是定义一个抽象类,个人可理解为 是为了规范子类中的所有方法一致性。
接口有两点要注意:
1、接口的实现类(子类)必须要有接口中所有的方法
2、接口的实现类(子类)实现的方法必须跟接口中所有的方法的名字一样
接口有两种实现方式:
import abc from abc import abstractmethod #使用抽象类实现,该类里面全是抽象方法 class Interface1(metaclass=abc.ABCMeta): @abstractmethod def show(self): pass # 使用普通类实现,该类里面全是没有实现逻辑的方法 class Interface2: def show(self): pass class Test(Interface1): def show(self): print('重写每一个方法,赋予逻辑') class Test2(Interface2): def show(self): print('重写每一个方法,赋予逻辑')
多态:
所谓多态指的是程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才决定。
简单理解: 一个函数的参数,通过传入参数的不同具备不同的意义;或者 + 号在不同的地方使用具备不同的意义,这就是多态。
多态的好处: OOP的开闭合原则,对添加代码开放,对修改代码闭合。
实现多态的方法: 1、使用继承; 2、使用接口
重载:
重新定义print输出的内容,使用__str__() 方法,该方法 return 回一个 输出的值。 在类里面定义该方法,直接输出实例对象,会输出 __str__() 方法返回的值
class Test: def __str__(self): return '这是重新定义输出的内容' test = Test() print(test) # 这是重新定义输出的内容
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种新的功能,以适应不同的数据类型。在一个类中具备了对运算符进行重载的多个方法,该方法可以重新定义两个实例对象相加或相减等运算的结果。
# encoding=utf-8 class Vector: def __init__(self, x, y): self.X = x self.Y = y # 重载print()方法 def __str__(self): return "[{0}, {1}]".format(self.X, self.Y) # 重载 + 运算符 def __add__(self,other): return Vector(self.X + other.X, self.Y + other.Y) # 重载 - 运算符 def __sub__(self,other): return Vector(self.X - other.X, self.Y - other.Y) # 重载 * 运算符 def __mul__(self,other): return Vector(self.X * other.X, self.Y * other.Y) # 重载 % 运算符 def __mod__(self,other): return Vector(self.X % other.X, self.Y % other.Y) v1 = Vector(5, 6) print(v1) # [5, 6] v2 = Vector(5, 6) v3 = Vector(3, 3) print(v2 + v3) # [8, 9] v4 = Vector(5, 6) v5 = Vector(3, 3) print(v4 - v5) # [2, 3] v6 = Vector(5, 6) v7 = Vector(3, 3) print(v6 * v7) # [15, 18] v8 = Vector(5, 6) v9 = Vector(3, 3) print(v8 % v9) # [2, 0]
自定义异常: 除了默认的异常类型,还可以使用 repr() 方法 在类里面自定义异常:
#encoding=utf-8 class MyException(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) # 自定义抛出异常信息 try: test = int(input('请输入年龄')) if test > 130: raise MyException('年龄不能大于130') # 自定义抛出异常 if test < 1: raise MyException('年龄不能小于1') # 自定义抛出异常 except ValueError as e: print('请输入数字') except MyException as a: # 使用自定义的异常类型 print(a.value)
迭代器: 使用 iter() 方法, 使某个集合转换为迭代对象, 使用next() 依次输出里面的值。通常跟for 循环配合使用;
list1 = [1,2,3,4,5,6,7,8] iter1 = iter(list1) print(next(iter1)) # 1 print(next(iter1)) # 2 for t in iter1: print(t)
列表生成器: 通过列表生成式,我们可以直接创建一个列表。但是受到内存限制,列表容量肯定是有限的。而且当创建的列表包含100万个数据时,会占用很大的内存,如果我们仅仅使用到前几个数据,那么后面元素占用的空间就浪费了。所以,将列表元素按照某种算法推算出来,不必创建完整的list, 从而节省大量的空间,这种一边循环一边计算的机制,称为生成器(Generator) 。
创建生成器的方法: 把一个列表生成式的 [] 改为 ()
list1 = [x for x in range(1,1000)] # 列表生成式 list2 = (x for x in range(1,1000)) # 列表生成器 print(list2)
创建函数生成器的方法:在一个函数内定一个 yield 语句,那个这个函数就变成了 Generator 。 函数生成器和函数的执行流程不一样,函数是顺序执行,遇到return或者执行完毕就返回。而变成generator的函数,在每次调用next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的yield语句处继续执行。 如果在 yield 语句前先遇到return ,会直接停止迭代并抛出异常。
def sum(): print('one') yield 1 print('two') yield 2 print('three') yield 3 print('end') test = sum() next(test) # one next(test) # two next(test) # three next(test) # end 超过迭代长度,报错
斐波那契数列的各种实现方法:
def fibo(n): a, b, n1 = 0, 1, 0 while n1 < n: a, b = b, a + b print(a) n1 = n1 + 1 fibo(5) # 1 1 2 3 5
import sys class Fibo: def __init__(self, max): self.max = max self.a, self.b, self.n = 0,1,0 def __iter__(self): #迭代器,返回自己 return self def __next__(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r else: sys.exit(0) for t in Fibo(5): print(t) # 1 1 2 3 5
def fibo(n): a, b, n1 = 0, 1, 0 while n1 < n: a, b = b, a + b yield a n1 = n1 + 1 for t in fibo(5): print(t) # 1 1 2 3 5
如何判断一个函数是否一个generator函数,使用 isgeneratorfunction 模块:
from inspect import isgeneratorfunction def fibo(n): a, b, n1 = 0, 1, 0 while n1 < n: a, b = b, a + b yield a n1 = n1 + 1 print(isgeneratorfunction(fibo)) # True
生成器的应用场景:
1、遍历输出多维列表(扁平化列表)
list1 = [1,[2,3,4],5,6,[7,[8,9]]] def flatten(list2): if type(list2) == list: for t in range(len(list2)): for e in flatten(list2[t]): yield e else: yield list2 for t in flatten(list1): print(t)
2、切割读取文件。直接对文件对象调用read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容:
def readFile(paths): block_size = 4096 * 2 with open(paths, mode='r+') as f: while True: block = f.read(block_size) if block: yield block else: return for t in readFile('test.txt'): with open('new.txt', mode='a+') as f2: f2.write(t)
生成器支持的方法:
close() 方法: 手动关闭生成器,关闭后再次调用迭代会报异常
def test(): yield 1 yield 2 yield 3 yield 4 t = test() print(next(t)) # 1 print(next(t)) # 2 t.close() print(next(t)) # 报停止迭代异常
send() 方法: 用于向生成器传入某个参数,yield会根据参数返回特定的值,使用该方法第一次调用的时候必须传 None
def test(): value = 0 while True: r = yield value if r == 'stop': break value = '得到某个值进行操作并返回:%s'%r t = test() print(t.send(None)) # 0 第一次调用必须传 None print(t.send('a')) # 得到某个值进行操作并返回:a print(t.send('b')) # 得到某个值进行操作并返回:b print(t.send('stop')) # 停止迭代报异常
throw() 方法: 用于使生成器抛出指定的异常类型,或自定义异常类型。调用该方法的时候,会消耗下一次迭代。
def test(): while True: try: yield '正常输出1' yield '正常输出2' print('迭代结束') except ValueError: print('值异常') except TypeError: break t = test() print(next(t)) # 正常输出1 print(next(t)) #正常输出2 t.throw(ValueError) # 值异常 print(next(t)) #正常输出2