作业 1. 三合一 2. 当天练习独立完成 3. 迭代器遍历图形控制器 class GraphicController: pass controller = CommodityController() controller.add_graphic("圆形") controller.add_graphic("矩形") controller.add_graphic ("三角形") for item in controller: print(item) 4. 将迭代器版本的商品控制器改为生成器yield实现 5. 对员工管理系统进行异常处理 employee_system 6. 完成mvc版本2048 -- 将核心算法移动到GameController类中 提示:全局变量改为实例变量 函数改为实例方法 -- 创建GameView类,根据wsad键移动数字.
#遍历图形控制器:一个自己的类怎么参与循环的过程 class GraphicIteror: def __init__(self, data): self.__data = data self.__index = -1 def __next__(self): if self.__index == len(self.__data) - 1: raise StopIteration() self.__index += 1 return self.__data[self.__index] class GraphicController: def __init__(self): self.__graphics = [] def add_graphic(self, gra): self.__graphics.append(gra) def __iter__(self): return GraphicIteror(self.__graphics) controller = GraphicController() controller.add_graphic("圆形") controller.add_graphic("矩形") controller.add_graphic("三角形") for item in controller: print(item) # for原理 iterator = controller.__iter__() while True: try: item = iterator.__next__() print(item) except StopIteration: break """ 1.父类iter()物理上代码不存在,即python找不到他,但思想上存在, 子类小鸭子原则,调用父类,执行子类。 面向对象父类约束与隔离的强大 2.next为什么不直接写入到与iter相同类中? 因为每个创建的对象都需要具有这个方法才能实现迭代, 将其单独抽离到另外一个类中,通过跨类减少代码的重复性 """
# 将迭代器版本的商品控制器改为迭代器yield实现 class CommodityController: def __init__(self): self.__comditys = [] def add_commodity(self, cmd): self.__comditys.append(cmd) def __iter__(self):#1995年就确定下的函数 # 生成代码的大致规则:了解 # 1. 将yield关键字前面的代码作为__next__函数体 # 2. 将yield关键字前面的数据作为__next__返回值 index = 0 yield self.__comditys[index] index += 1 yield self.__comditys[index] index += 1 yield self.__comditys[index] controller = CommodityController() controller.add_commodity("屠龙刀") controller.add_commodity("倚天剑") controller.add_commodity("芭比娃娃") for item in controller: print(item) # for原理 iterator = controller.__iter__()#调试直接不进入这个函数 while True: try: item = iterator.__next__()#调试进入yield print(item) except StopIteration: break
from bll import EmployContler from usl import EmployView import sys print(sys.path) if __name__ == '__main__': try: ctrontrl = EmployContler() view1 = EmployView(ctrontrl) view1.main() except: print("这叫全局异常处理,不能针对有效细节,不是我们想要的异常处理") """ 就算没有手动标蓝,也不报错,原因是默认这个文件夹下面导入路径 """
class EmployModel: def __init__(self, name="", money=0, eid=0, id=""): self.name = name self.money = money self.eid = eid self.id = id def __str__(self): return f"姓名{self.name},工资{self.money},编号{self.eid}" def __eq__(self, other): return self.id == other
from model import EmployModel class EmployView: def __init__(self, ctrl): self.__ctrl = ctrl # type:EmployContler def __display(self): print("按1键录入员工信息") print("按2键显示员工信息") print("按3键修改员工信息") print("按4键删除员工信息") def __select_maue(self): item = input("请输入选项") if item == "1": self.__input() elif item == "2": self.__show() elif item == "3": self.__modity_emp() elif item == "4": self.__delete_employ() def __get_right(self, message): # 妙啊 while True: try: num = int(input(message)) return num except: print("输入有误") def __input(self): emp = EmployModel() emp.name = input("请输入员工姓名") emp.money = self.__get_right("请输入员工薪资") emp.eid = self.__get_right("请输入员工编号") self.__ctrl.storage(emp) def __show(self): for item in self.__ctrl.list_employ: print(item) def __modity_emp(self): emp = EmployModel() emp.id = self.__get_right("请输入要修改的id") emp.name = input("请输入要修改的姓名") emp.eid = self.__get_right("请输入要修改的编号") emp.money = self.__get_right("请输入要修改的薪资") if self.__ctrl.modity_employ(emp): print("修改成功") else: print("修改失败") def __delete_employ(self): id = self.__get_right("请输入要修改的id") if self.__ctrl.delete1_emp(id): print("删除成功") else: print("删除失败") def main(self): while True: self.__display() self.__select_maue()
from model import EmployModel class EmployContler: __startid = 0 @classmethod def __set_id(cls, emp: EmployModel): cls.__startid += 1 emp.id = cls.__startid def __init__(self): self.__list_employ = [] @property def list_employ(self): return self.__list_employ def storage(self, emp: EmployModel): EmployContler.__set_id(emp) self.__list_employ.append(emp) def modity_employ(self, emp) -> bool: for item in self.__list_employ: if item.id == emp.id: # 寻址可以修改item 从而修改list_employ 妙啊 item.name = emp.name item.eid = emp.eid item.money = emp.money return True return False def delete1_emp(self, id) -> bool: if id in self.__list_employ: self.__list_employ.remove(id) return True return False
from bll import GameController from usl import GameView # import bll 为什么这里将整个模块导入不行 # import usl ctr1 = GameController() view1 = GameView(ctr1) view1.main()
from bll import GameController class GameView: def __init__(self, controller): self.__controller = controller def __key_conter(self): key = input("wasd") if key == "w": self.__controller.move_up() elif key == "s": self.__controller.move_down() elif key == "a": self.__controller.move_left() elif key == "d": self.__controller.move_right() def __map_square(self): for line in self.__controller.map: for item in line: print(item, end="\t") # 元素之间弹性空格 print() # 换行 def main(self): while True: self.__map_square() self.__key_conter()
class GameController: def __init__(self): self.__map = [ [2, 0, 0, 2], [4, 2, 0, 2], [2, 4, 2, 4], [0, 4, 0, 4], ] self.__list_merge = None @property def map(self): return self.__map def __zero_to_end(self): for i in range(len(self.__list_merge) - 1, -1, -1): if self.__list_merge[i] == 0: del self.__list_merge[i] self.__list_merge.append(0) def __merge(self): self.__zero_to_end() for i in range(len(self.__list_merge) - 1): if self.__list_merge[i] == self.__list_merge[i + 1]: self.__list_merge[i] += self.__list_merge[i + 1] del self.__list_merge[i + 1] self.__list_merge.append(0) def move_left(self): for line in self.__map: self.__list_merge = line self.__merge() def move_right(self): for line in self.map: self.__list_merge = line[::-1] self.__merge() line[::-1] = self.__list_merge def __square_matrix_transposition(self): for c in range(1, len(self.__map)): # 1 2 3 for r in range(c, len(self.__map)): self.__map[r][c - 1], self.__map[c - 1][r] = self.__map[c - 1][r], self.__map[r][c - 1] def move_up(self): self.__square_matrix_transposition() self.move_left() self.__square_matrix_transposition() def move_down(self): self.__square_matrix_transposition() self.move_right() self.__square_matrix_transposition() if __name__ == '__main__': # 测试 controller = GameController() controller.move_down() controller.move_up() controller.move_left() controller.move_right() print(controller.map)
生成器 generator
-
定义:能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。
-
作用:在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从 而节省内存空间。数据量越大,优势越明显。
-
以上作用也称之为延迟操作或惰性操作,通俗的讲就是在需要的时候才计算结果, 而不是一次构建出所有结果。
生成器函数
-
定义:含有 yield 语句的函数,返回值为生成器对象。
-
语法
-- 创建:
def 函数名():
…
yield 数据
…
-- 调用:
for 变量名 in 函数名():
语句
-
说明:
-- 调用生成器函数将返回一个生成器对象,不执行函数体。
-- yield 翻译为”产生”或”生成”
-
执行过程:
(1) 调用生成器函数会自动创建迭代器对象。
(2) 调用迭代器对象的next()方法时才执行生成器函数。
(3) 每次执行到 yield 语句时返回数据,暂时离开。
(4) 待下次调用next()方法时继续从离开处继续执行。
-
原理:生成迭代器对象的大致规则如下
-- 将 yield 关键字以前的代码放在 next 方法中。
-- 将 yield 关键字后面的数据作为 next 方法的返回值。
""" MyRange 1.0 迭代器实现 MyRange 2.0 yield实现 MyRange 3.0 生成器实现必须会(浅出) range产生的海量数据不用担心撑爆内存,进来一次,排出一次 """ # 实用价值:循环一次 计算一次 返回一次 以后工作当中就这么用 def my_range(stop): number = 0 while number < stop: yield number number += 1 for number in my_range(5): print(number) # 0 1 2 3 4 # 调用但不执行进入iter函数体,原因是有yield, # 而是返回生成器对象,调试看对象 mr = my_range(5) print(type(mr)) # <class 'generator'> iterator = mr.__iter__() # <class 'generator'> print(type(iterator)) while True: try: item = iterator.__next__() print(item) # 0 1 2 3 4 except StopIteration: break """ 伪代码:为了看清调试原理 class generator: # 生成器 = 可迭代对象+迭代器(核心) def __iter__(self): # 可迭代对象 return self def __next__(self): # 迭代器 index += 1 return index """
""" 练习 1:定义函数,在列表中找出所有偶数 [43,43,54,56,76,87,98] 练习 2. 定义函数,在列表中找出所有字符串 [43,"悟空",True,56,"八戒",87.5,98] """ list01 = [43, 43, 54, 56, 76, 87, 98] list02 = [43, "悟空", True, 56, "八戒", 87.5, 98] # -- 传统思想:创建容器存储所有偶数,return容器 #定义好容器,一次性计算好返回计算结果,浪费性能----调试看 def get_evens1(list_number): result = [] for item in list_number: if item % 2 == 0: result.append(item) return result re = get_evens1(list01) for item in re: print(item) # -- 生成器思想:一个个推算偶数,yield 偶数 #一次一次迭代计算好返回计算结果,节约性能----调试看 def get_num(list1): index = 0 while index < len(list01): if list01[index] % 2 == 0: yield list1[index] index += 1 # -- 生成器思想:一个个推算字符串,yield 字符串 def get_number(list1): index = 0 while index < len(list01): if type(list1[index]) == str: yield list1[index] index += 1 for number in get_num(list01): print(number) for number in get_number(list02): print(number)
""" 练习 1:定义函数,在列表中找出所有偶数 [43,43,54,56,76,87,98] -- 传统思想:创建容器存储所有偶数,return容器 -- 生成器思想:一个个推算偶数,yield 偶数,优点是内存不崩溃,缺点是不存数据 吃多少,排多少 """ # 传统思想: list01 = [43, 43, 54, 56, 76, 87, 98] def oushu1(data): list02 = [] for item in data: if item % 2 == 0: list02.append(item) return list02 print(oushu1(list01)) # 生成器思想1 (自己捣鼓的) def oushu2(data): number = 0 while number < len(data): if data[number] % 2 == 0: yield data[number] number += 1 for item in oushu2(list01): print(item) # 0 1 2 3 4 # 生成器思想2 def get_evens2(list_number): for item in list_number: if item % 2 == 0: yield item re = get_evens2(list01) #这加断点,函数不上去,惰性,直接到下一行 for item in re: print(item)
""" 练习 2. 定义函数,在列表中找出所有数字 [43,"悟空",True,56,"八戒",87.5,98] 结论(思想):函数有一个结果用return 函数有多个结果用yelid """ # 传统思想: list01 = [43, "悟空", True, 56, "八戒", 87.5, 98] def get_num1(data): list02 = [] for item in data: if type(item) in (int, float): list02.append(item) return list02 # 生成器思想 def get_num2(list_number): for item in list_number: if type(item) in (int, float): yield item for item in get_num1(list01): # 老式:函数体一顿循环 然后跳到for 又是一顿循环 print(item) for item in get_num2(list01): # 新式:函数体与for轮流交替,上去下来,一上一下 print(item)
内置生成器
枚举函数 enumerate
-
语法:
for 变量 in enumerate(可迭代对象):
语句
for 索引, 元素 in enumerate(可迭代对象):
语句
-
作用:遍历可迭代对象时,可以将索引与元素组合为一个元组。
""" 内置生成器:不用担心性能,它是迭代思想 """ list01 = [43, 43, 54, 56, 76] # 以前 # 从头到尾读 -- 读取数据方便 for item in list01: print(item) # 非从头到尾读 -- 有索引能修改 for i in range(len(list01)): if list01[i] % 2 == 0: list01[i] += 1 # 现在 # 读写数据时使用 list01 = [43, 43, 54, 56, 76] for i, item in enumerate(list01): # 拆包元组 print(i, item) for item in enumerate(list01): # 不拆包元组 print(item)#打印元组,很少用,因为不需要她两组合一起用 dict01 = {"a": "A", "b": "B"} for i, key in enumerate(dict01): # 拆包成key 与 i print(i, key, dict01[key])
""" 练习 1:将列表中所有奇数设置为 None 练习 2:将列表中所有偶数自增 1 """ list01 = [43, 43, 54, 56, 76] for i, item in enumerate(list01): if item % 2 == 1: list01[i] = None # item = None 只能读,不能改 print(list01) list01 = [43, 43, 54, 56, 76] for i, item in enumerate(list01): # itere+回车自动生成 if item % 2 == 0: list01[i] += 1 # item = None 只能读,不能改 print(list01)
zip
-
语法:
for item in zip(可迭代对象 1, 可迭代对象 2….):
语句
-
作用:将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小
的可迭代对象决定。
""" zip 作用:将多个可迭代对象中对应的元素组合成一个个元组, 生成的元组个数由最小 的可迭代对象决定,木桶短板原则。 """ list_name = ["gsx", "wzg", "zwj"] list_age = [22, 26] # for 变量 in zip(可迭代对象1,可迭代对象2) for item in zip(list_name, list_age): print(item) #('gsx', 22) #('wzg', 26) # 应用:矩阵转置 其中3.0一句话到位 map = [ [2, 0, 0, 2], [4, 2, 0, 2], [2, 4, 2, 4], [0, 4, 0, 4] ] # 1.0 # new_map = [] # for item in zip(map[0], map[1], map[2], map[3]):#手动拆开 # new_map.append(list(item)) # print(new_map) # 2.0 # new_map = [] # for item in zip(*map):#自动拆开:实参拆开 # new_map.append(list(item)) # 3.0 列表推导式 new_map = [list(item) for item in zip(*map)] print(new_map)
""" 练习:使用学生列表封装以下三个列表中数据 list_student_name = ["悟空", "八戒", "白骨精"] list_student_age = [28, 25, 36] list_student_sex = ["男", "男", "女"] """ # 1.0 添加数据需要修改代码,不灵活 class Student: def __init__(self, name="", age=0, sex=""): self.name = name self.age = age self.sex = sex def __str__(self): return f"{self.name},{self.age},{self.sex}" # list_student_name = ["悟空", "八戒", "白骨精"] # list_student_age = [28, 25, 36] # list_student_sex = ["男", "男", "女"] # list_student = [] # for item in zip(list_student_name, list_student_age, list_student_sex): # stu = Student(item[0],item[1],item[2]) # list_student.append(stu) # print(list_student) # 2.0 添加数据不需要修改代码,灵活 list_datas = [ ["悟空", "八戒", "白骨精"], [28, 25, 36], ["男", "男", "女"] ] # list_student = [] # for data in zip(*list_datas):#自动拆开每一个元素,依次取每个元素的索引对应值 # stu = Student(*data)#自动拆开赋值给实例变量 # list_student.append(stu) # 3.0推导式实现 list_student = [Student(*data) for data in zip(*list_datas)] print(list_student)
# 深度体会生成器价值 class Employee: def __init__(self, eid, did, name, money): self.eid = eid self.did = did self.name = name self.money = money list_employees = [ Employee(1001, 9002, "师父", 60000), Employee(1002, 9001, "孙悟空", 50000), Employee(1003, 9002, "猪八戒", 20000), Employee(1004, 9001, "沙僧", 30000), Employee(1005, 9001, "小白龙", 15000), ] # 练习1:定义函数,在员工列表中查找did是9001的所有员工:返回多个 def find_emp_did(): for item in list_employees: if item.did == 9001: yield item # 调用函数不执行函数体,返回生成器对象(推算数据) result = find_emp_did() for item in result: print(item.__dict__) # 练习2:定义函数,在员工列表中查找薪资大于20000的所有员工:返回多个 def find_emp_money(): for item in list_employees: if item.money > 20000: yield item result = find_emp_money() for item in result: print(item.__dict__) # 练习3:定义函数,在员工列表中查找eid是1003的员工 :返回一个 用return def find_emp_eid(): for item in list_employees: if item.eid == 1003: return item emp = find_emp_eid() print(emp.__dict__) # 缺点1:生成器的对象只能使用(for)一次,因为用next到最后了 # result = find_emp_money() # for emp in result: # print(emp.__dict__) # for emp in result: # 不执行,调试看看 # print(emp.__dict__) # 缺点2:不能定位元素(不支持索引与切片),只能for 然后一个个next # print(result[1]) print(type(find_emp_money())) # <class 'generator'> # 解决:将生成器转换为容器,就不用使用for了啊,非常灵活的用, # 但缺点是列表占内存,for不占内存,但只能next一个一个用 list_result = list(find_emp_money()) print(list_result) # 练习4:定义函数,在员工列表中查找所有员工的姓名 def find_employees_name(): for item in list_employees: yield item.name re = find_employees_name() # 只产生re一个生成器对象,不可以for # 两次,除非调用函数再产生个re给另外一个for用 list_result = list(re) print(list_result)
生成器表达式
-
定义:用推导式形式创建生成器对象。
-
语法:变量 = ( 表达式 for 变量 in 可迭代对象 [if 真值表达式] )
""" 列表推导式 列表 = [对变量操作 for 变量 in 可迭代对象] 列表 = [对变量操作 for 变量 in 可迭代对象 if 条件] 优点: 可以定位(索引切片) 反复使用 缺点: 占用内存 生成器表达式 生成器 = (对变量操作 for 变量 in 可迭代对象) 生成器 = (对变量操作 for 变量 in 可迭代对象 if 条件) 优点: 节省内存 缺点: 不可以定位(索引切片) 不反复使用 """ list01 = [432, 5, 54, 6, 76, 87] # 列表推导式 需求:将大于10的元素存储新列表 # 调试上上上....下下下...... list02 = [item for item in list01 if item > 10] for item in list02: print(item) # 生成器表达式:调试上下上下上下...... generator02 = (item for item in list01 if item > 10) for item in generator02: print(item) """ 生成器表现形式1:推导式 语法:变量 = ( 表达式 for 变量 in 可迭代对象 [if 真值表达式] ) 生成器表现形式2:普通形式 -- 创建: def 函数名(): … yield 数据 … -- 调用: for 变量名 in 函数名(): 语句 生成器核心:计算一次,返回一次,执行一次 """
""" 练习1:使用生成器表达式在列表中获取所有字符串. list01 = [43, "a", 5, True, 6, 7, 89, 9, "b"] 练习2:在列表中获取所有整数,并计算它的平方. """ list01 = [43, "a", 5, True, 6, 7, 89, 9, "b"] all_str = (item for item in list01 if type(item) == str) for item in all_str: print(item) all_number = (item ** 2 for item in list01 if type(item) == int) for item in all_number: print(item)
小结 - 生成器 生成器函数 def 函数名(): ... yield 数据 ... for item in 函数名(): ... 适用性:函数有多个结果使用yield 如果需要反复使用,定位数据,使用下列调用方式: 变量 = 容器(函数名()) 生成器表达式 当使用列表推导式时,如果不需要反复使用且定位数据, 应该改为生成器表达式 笔试题: 请简述生成器与迭代器的关系? 生成器本质就是迭代器+可迭代对象 当使用生成器时,就是重复调用__next__函数,计算一次返回一次数据, 不将所有数据存储在内存中.
class Commodity: def __init__(self, cid, name="", price=0): self.cid = cid self.name = name self.price = price list_commodity_infos = [ Commodity(1001, "屠龙刀", 10000), Commodity(1002, "倚天剑", 10000), Commodity(1003, "金箍棒", 52100), Commodity(1004, "口罩", 20), Commodity(1005, "酒精", 30), ] # 要求:使用生成器函数(反复使用)和生成器表达式完成(用一次) # 练习1:查找商品编号小于1003的所有商品 # 1 def find_cmd_cid(): for item in list_commodity_infos: if item.cid < 1003: yield item # 调用函数不执行函数体,返回生成器对象(推算数据) for item in find_cmd_cid(): print(item.__dict__) # 2 generator02 = (item for item in list_commodity_infos if item.cid < 1003) # 变成小括号 for item in generator02: print(item.__dict__) # 练习2:查找商品名称大于2个字的所有商品 # 1 def find_emp_name(): for item in list_commodity_infos: if len(item.name) > 2: yield item # 调用函数不执行函数体,返回生成器对象(推算数据) for item in find_emp_name(): print(item.__dict__) # 2 generator02 = (item for item in list_commodity_infos if len(item.name) > 2) # 变成小括号 for item in generator02: print(item.__dict__) # 练习3:查找商品单价大于10000元的所有商品名称 # 1 def get_commodity_price(): for cmd in list_commodity_infos: if cmd.price > 10000: yield cmd.name for item in get_commodity_price(): print(item) # 2 commodity_names = (cmd.name for cmd in list_commodity_infos if cmd.price > 10000) for item in commodity_names: print(item) # 练习4:查找单价最便宜的商品信息(自定义函数) def get_min_by_price(): min_value = list_commodity_infos[0] for i in range(1, len(list_commodity_infos)): if min_value.price > list_commodity_infos[i].price: min_value = list_commodity_infos[i] return min_value min_value = get_min_by_price() print(min_value.__dict__)