迭代器
迭代器
迭代:# 更新换代(其实也是重复)的过程,每一次的迭代都必须基于上一次的结果(上一次与这一次之间必须是有关系的)
迭代器:# 迭代取值的工具
为什么用迭代器:# 迭代器提供了一种可以不依赖索引取值的方式
# 会一直打印0,记得停止# n = 0# while True:#print(n)# 重复 + 每次迭代都是基于上一次的结果而来的l = [1, 2, 3, 4, 5]s = 'hello'n = 0while n < len(s):print(s[n])n += 1# h# e# l# l# o
迭代小案例
可迭代对象
'''可迭代对象只要内置有 __iter__ 方法的都叫做可迭代对象(读:双下iter)补充: 针对双下划线开头结尾的方法,推荐读“双下 + 方法名”基本数据类型中,是可迭代对象的有(str, list, tuple, dict, set), 文件对象(执行 __iter__ 之后还是本身,__next__之后还是他本身)可迭代对象执行内置的 __iter__ 方法,得到的就是该对象的迭代器对象'''
n = 1f = 1.1s = 'hello'l = [1,2,34,]t = (1,2,34)s1 = {1,2,3,4}d = {'name':'jason'}f1 = open('xxx.txt','w',encoding='utf-8')## res = s.__iter__() # res = iter(s)## print(s.__len__()) # 简化成了len(s)# res1 = l.__iter__() # res1 = iter(l)# res2 = f1.__iter__() # res2 = iter(f1)# print(res,res1,res2)# print(f1)# 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象
实验
迭代器对象与可迭代对象的区别
'''迭代器对象和可迭代对象有什么共同点迭代器对象1.内置有双下iter( __iter__) 方法2.内置有 __next__ 方法ps:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象'''
__iter__转换迭代器对象
l = [1, 2, 3, 4]# 生成一个迭代器对象iter_l = l.__iter__()# 迭代器取值 调用__next__print(iter_l.__next__())print(iter_l.__next__())print(iter_l.__next__())print(iter_l.__next__())# 1# 2# 3# 4# print(iter_l.__next__()) # 取完了 直接报错d = {'name':'jason','password':'123','hobby':'泡m'}# 将可迭代对象d转换成迭代器对象iter_d = d.__iter__()# 迭代器对象的取值 必须用__next__print(iter_d.__next__())print(iter_d.__next__())print(iter_d.__next__())# name# password# hobby# print(iter_d.__next__()) # 取完了 报错StopIterationf1 = open('test1.txt', 'r', encoding='utf-8')# 调用f1内置的__iter__方法iter_f = f1.__iter__()print(iter_f is f1)# True"""迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)"""print(f1 is f1.__iter__().__iter__().__iter__().__iter__())# True
列表-字典-文件对象举例
# 迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)
疑问:# __iter__方法就是用来帮我们生成迭代器对象,而文件对象本身就是迭代器对象,为什么还内置有__iter__方法??? ---见for循环原理
异常处理
d = {'name': 'jason', 'password': '123', 'hobby': '泡m'}iter_d = d.__iter__()# 异常处理while True:try:print(iter_d.__next__())except StopIteration:print('老母猪生不动了')break# name# password# hobby# 老母猪生不动了
异常处理
迭代器与迭代器对象总结
'''可迭代对象:内置有 __iter__ 方法迭代器对象:既内置有__iter__ 方法,也内置有__next__方法迭代取值的优点与缺点:优点:不依赖于索引取值内存中永远只占一份空间,不会导致内存溢出缺点:不能获取指定的元素取完之后会报StopIteration错..'''
for循环内部原理
'''for 循环内部的本质1.将in后面的对象调用 __iter__ 转换成迭代器对象 --> 所以前面的疑问,文件对象本来就是迭代器对象还有 __iter__ 方法2.调用__next__ 迭代取值3.内部有异常捕获,StopIteration,当__next__报这个错,自动结束循环'''
内部原理同上面异常处理里的案例
生成器
生成器:# 用户自定义的迭代器,本质就是迭代器
注意点:# 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行,# yield后面跟的值就是调用迭代器__next__方法你能得到的值,# yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
yield 返回值(体现上面的注意点)
def func():print('first')yield # 函数内如果有yield 关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 ***print('second')yield 2 # yield 后面跟的值就是调用迭代器 __next__ 方法你能得到的值print('third')yield 3, 4, 5 # 既可以返回一个值,也可以返回多个值,并且多个值也是按照元组的形式返回g = func() # 生成器初始化:将函数变成迭代器# g.__iter__()print(g.__next__())# first# Noneprint(g.__next__())# second# 2print(g.__next__())# third# (3, 4, 5)
利用yield返回值
小案例
# 自定义range 功能for i in range(10):print(i, end='- ')print()def my_range(start, end, step=1): # 只传一个参数的,range,判断参数个数 搜:python只传一个参数的rangewhile start < end:yield startstart += stepfor i in my_range(1, 10, 3):print(i)# 1# 4# 7
自定义实现range
yield表达式形式传入参数
# yield 表达式形式(传参的第三种方式,直接传参,闭包)def dog(name):print("%s 准备开始吃" %name)while True:food = yieldprint("%s 吃了 %s" %(name, food))print(dog)# <function dog at 0x000001C22A10BA60># 当函数内有yield 关键字的时候 调用该函数 不会执行函数体代码# 而是将函数变成生成器g = dog("egon") # 把函数变成生成器,函数体没有运行g.__next__() # 不运行这个直接运行下面的会直接报错 TypeError: can't send non-None value to a just-started generator# 必须将代码运行至 yield 才能为其传值# egon 准备开始吃g.send('狗不理包子') # 给yield 左边的变量传参,触发了 __next__ 方法# egon 吃了 狗不理包子g.__next__()# egon 吃了 Noneg.send('饺子')# egon 吃了 饺子
利用yield传入参数
总结yield并与return作对比
'''总结yield:1.yield 提供了一种自定义生成器的方式2.yield 会帮你将函数的运行状态暂停住3.yield 可以返回值与return 之间的异同点:相同点:1.都可以返回值,并且都可以返回多个,返回多个都是包装成元组不同点:1.yield 可以返回多次值,而return 只能返回一次函数立即结束2.yield还可以接收外部传入的值'''
总结与对比
常用内置方法
常见数学内置函数 abs 绝对值 pow 求平方 round 四舍五入 divmod将运算结果除数与余数存入元组
# abs 求绝对值print(abs(-10.11))# 10.11# pow 平方print(pow(2, 4))# 16# round 四舍五入print(round(3.1656))# 3# divmod 将运算结果除数与余数存入元组(可用于分页器)print(divmod(100, 11))# (9, 1)all_count = 101counts_each_page = 5total_pages, more = divmod(all_count, counts_each_page)if more:total_pages += moreprint(total_pages)# 21 --> 共101条内容,按每页5条来分,需要分21页
abs pow round divmod
数据类型进制转换 isinstance(判断数据类型) all(传过来容器类型必须全部都是True才返回True) any(有一个是True就返回True) bool(将传进来参数变成布尔类型) bin(十转二) oct(十转八) hex(十转十六) int(把指定类型转成十进制)
# isinstance 判断数据类型,后面统一用该方法判断对象是否属于某一个数据类型n = 1print(type(n))print(isinstance(n, list)) # 判断对象是否属于某个数据类型# <class 'int'># False# all 传过来的容器类型里必须全部是True,否则返回False# any 只要一个True,就返回Truel = [0, 1, 2]print(all(l)) # 只要有一个是False,就返回Falseprint(any(l))# False# True# boolprint(bool(1))print(bool(0))# True# False# bin oct hex 将十进制转换成对应的进制print(bin(10))print(oct(10))print(hex(10))# 0b1010 二进制# 0o12 八进制# 0xa 十六进制# int 可以把指定进制转成十进制print(int('0b1010', 2))# 10
isinstance all any bool bin oct hex int
字符编码 ascii (个人觉得没啥用) chr(将数字转换成ascii码 对应的字符) ord (将字符按照ascii码 转成数字) bytes(转成二进制)
# ascii 个人觉得没啥用print(ascii('a'))# 'a'print(ascii(65))# 65print(ascii('c'))# 'c'# chr ord 将数字转换成ascii 码表对应的字符 将字符按照ascii 码表转成对应的数字print(chr(65))# Aprint(ord('A'))# 65# bytes 转换成二进制# s = b'hello'# print(s)# # b'hello's = 'hello'print(s.encode('utf-8'))print(bytes(s, encoding='utf-8'))# b'hello'# b'hello'
ascii chr ord bytes
可迭代对象常见内置函数 slice 切片(生成的是老母猪,节省空间) enumerate 给可迭代对象生成索引(假索引)
# slice 切片myslice = slice(5) # 设置截取5个元素的切片arr = range(10)print(arr)print(arr[myslice]) # 截取5个元素print(arr)# range(0, 10)# range(0, 5)# range(0, 10)# enumerate 枚举 可以指定起始位l = ['a', 'b', 'c']for i, j in enumerate(l, 1):print(i, j)# 1 a# 2 b# 3 c
slice enumerate
执行字符串中的python语句以及python字符串格式化 eval (执行字符串中的python语句,不够强大) exec (执行python中的语句,能执行逻辑的) format(格式化字符串)
# eval exec 执行字符串里的代码,eval 不支持逻辑代码,只支持一些简单的python代码, exec可以执行逻辑代码s = """print('hello baby~')# x = 1# y = 2# print(x + y)"""# eval(s)# hello baby~exec(s)# hello baby~# format 三种玩法# ------ {} 占位 --------print('{} like {}'.format('tank', 'jason'))# tank like jason# ------ {index} 索引 --------print('{0} like {2}'.format('tank', 'egon', 'jason'))# tank like jason# ------ {name} 指名道姓(关键字) --------print('{name} is {age} years old.'.format(age=18, name='jason'))# jason is 18 years old.
eval exec format
对象命名空间 callable(对象是否可调用) dir (获取该对象名称空间里的名字) globals (查看全局名称空间的名字) locals (返回当前位置名称空间的名字) help (查看函数的注释)
# callable 可调用的,可以加括号执行相应功能def index():passl = [1, 2, 3, 4, 5]print(callable(index))print(callable(l))# True# False# dir 获取当前对象名称空间里面的名字(所有他能够支持的名字),可以是任何对象l = [1, 2, 3]print(dir(l))# ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']# import test# print(dir(test))# print(test.name)# globals 无论在哪,查看的都是全局名称空间# locals 当前语句在那个位置,就会返回哪个位置所存储的所有名字def index():'''index的注释:return: 没有返回值'''username = "局部名称空间里的username"print(locals()) # 当前语句在那个位置,就会返回哪个位置所存储的所有名字print(globals()) # 不受位置影响# {'__name__': '__main__'-------省略一大堆------- 'l': ['a', 'b'], 'i': 2, 'j': 'b'.....>}index()# {'username': '局部名称空间里的username'}# help 查看函数注释print(help(index))# Help on function index in module __main__:## index()#index的注释#:return: 没有返回值## None
callable dir globals locals help
面向过程编程
概述:# 面向过程就类似于流水线设置,将复杂问题分步实现
优点:# 可以将复杂的问题流程化,从而简单化
缺点:# 可扩展性差,如有改动,可能会涉及超大面积更改
小案例(牵一发动全身)
# 获取用户登录def get_info():while True:username = input(">>>:").strip()if not username.isalpha(): # 判断字符串不能包含数字print('不能包含数字')continuepassword = input('>>>:').strip()confirm_password = input("confirm>>>:").strip()if password == confirm_password:operate_data(username,password)breakelse:print('两次密码不一致')# 2.处理用户信息def operate_data(username,password):# jason|123res = '%s|%s\n'%(username,password)save_data(res,'userinfo.txt')# 3.存储到文件中def save_data(res,file_name):with open(file_name,'a',encoding='utf-8') as f:f.write(res)def register():get_info()register()
正常版注册
# 在用户注册时候让用户选择用户类型# 1.获取用户输入def get_info():while True:username = input(">>>:").strip()if not username.isalpha(): # 判断字符串不能包含数字print('不能包含数字')continuepassword = input('>>>:').strip()confirm_password = input("confirm>>>:").strip()if password == confirm_password:# 改动 ---------------------------------------------d = {'1':'user','2':'admin'}while True:print("""1 普通用户2 管理员""")choice = input('please choice user type to register>>>:').strip()if choice not in d:continueuser_type = d.get(choice)# 函数的参数个数也要改变operate_data(username,password,user_type)break# 改动 ----------------------------------------------else:print('两次密码不一致')# 2.处理用户信息# 函数定义时的的参数个数也要改变 ------------------------------------------def operate_data(username,password,user_type):# jason|123# 拼接也要改动 ------------------------------------------res = '%s|%s|%s\n'%(username,password,user_type)save_data(res,'userinfo.txt')# 3.存储到文件中def save_data(res,file_name):with open(file_name,'a',encoding='utf-8') as f:f.write(res)def register():get_info()register()
扩展让用户可选择用户类型
扩展练习题(面试题)
1.请判断下面代码中的 res 是什么
def add(n, i):return n + idef test():for i in range(4):yield ig = test()for n in [1, 10]:g = (add(n, i) for i in g)res = list(g)# A. res = [10, 11, 12, 13]# B. res = [11, 12, 13, 14]# C. res = [20, 21, 22, 23]# D. res = [21, 22, 23, 24]
# C. res = [20, 21, 22, 23]
答案
2.请判断下面程序会输出什么
# 已经提前把里面的东西取出来了
解析
3.利用生成器表达式读取文件行数
'''需求: 读取一个文件并返回每行数据的长度'''with open('test1.txt', 'w', encoding='utf-8') as f:for line in range(1000):f.write(f'www{line}aaa' * (line + 1) + '\n')
# 列表推导式: 处理数据量大的文件会导致内存溢出.res = [len(line) for line in open('test1.txt', 'r', encoding='utf-8')]# print(res)# 生成器表达式: 处理数据量大的文件推荐使用.res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8'))print(res2) # <generator object <genexpr> at 0x000002B3748FD0A0>print(next(res2))print(next(res2))
解决方案
4.写出下面代码的执行结果
# 请写出下面代码的执行结果def mutipliers():return [lambda x: i*x for i in range(4)]print([m(2) for m in mutipliers()])
# [6,6,6,6] ---> 题目的正确答案def multipliers():return [lambda x, i=i: i*x for i in range(4)]# 0, 1, 2, 3# [func(x): return 0*x, func(x): return 1*x,# func(x): return 2*x, func(x): return 3*x, ]print([m(2) for m in multipliers()]) # [0, 2, 4, 6]# [func(x): return 0*2, func(x): return 1*2,# func(x): return 2*2, func(x): return 3*2, ]# [0, 2, 4, 6]# [6, 6, 6, 6]# 闭包函数的延迟绑定# 在内层函数执行时才会绑定变量idef multipliers2():list1 = []for i in range(4):def func(x, i=i):return x * ilist1.append(func)return list1print([m(2) for m in multipliers2()]) # [0, 2, 4, 6]# [0, 2, 4, 6]
思路(根据思路题目更改答案变更)