迭代器, 可迭代对象, 迭代器对象, for循环内部原理, 生成器, 面试题, 生成器表达式, 内置函数补充, 面向过程编程
迭代器
什么是迭代器?
- 迭代:更新换代(重复)的过程,每次迭代都必须基于上一次的结果
- 迭代器:迭代取值的工具
为什么要用迭代器?
- 迭代器给你提供了一种不依赖于索引取值的方式
如何用迭代器?
# 重复且每次迭代都是基于上一次结果而来的 l = [1,2,3,4] n = 0 while n < len(l): print(l[n]) n += 1
需要迭代取值的数据类型 : 字符串/列表/元祖/字典/集合
可迭代对象
定义:只要内置有__iter__方法,或者__next__方法的都叫做可迭代对象
补充:针对双下划线开头/双下划线结尾的方法,我们推荐用 双下 + 方法名来读
基本数据类型中,可迭代对象有:
- str,list,tuple,set,dict
- 文件对象(执行内置的iter后还是它本身,没有出现任何变化)
注意 :
- 文件对象本身就是可迭代对象
- 文件对象为f = open('XXX.txt', 'w', encoding='utf-8')
- 可迭代对象执行内置的__iter__方法后得到的就是该对象的迭代器对象
l = [1,2,3,4] # 生成一个迭代器对象 iter_1 = l.__iter__() # 迭代器取值,调用__next__ print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) # 取完了之后就报错------StopIteration
迭代器对象
定义:
- 1.内置有__iter__方法
- 2.内置有__next__方法
ps:迭代器一定是可迭代对象,而可迭代对象不一定是迭代器对象
注意点:可迭代对象仅仅内置有__iter__方法
迭代取值:
- 优点:
- 1.不依赖于索引取值
- 2.内存中永远只占一份空间,不会导致内存溢出
- 缺点:
- 1.不能够获取指定的元素
- 2.取完之后会报错----StopIteration
例子:
d = {'name':'jason','password':123,'hobby':'XXX'} # 将可迭代对象d转换为迭代器对象 iter_d = d.__iter__() # 迭代器对象的取值,必须用__next__ print(iter_l.__next__())
f = open('xxx.txt', 'w', encoding='utf-8') # 调用f内置的__iter__方法 iter_f = f1.__iter__() print(iter_f1 is f)
迭代器对象执行__iter__方法之后得到的还是迭代器对象本身(重要)
思考:__iter__方法就是用来帮我们生成迭代器对象,而文件对象本身就是迭代器对象,为什么还内置iter方法?
小技巧:
d = {'name':'jason','password':123,'hobby':'XXX'} iter_d = d.__iter__() print(iter_d.__next__()) # 异常处理 while True: try: print(iter_d.__next__()) # 这段代码是可能会报的错 except StopIteration print('取值已到达上限') break
文件对象迭代取值:
f = open('XXX.txt', 'r', encoding='utf-8') iter_f = f.__iter__() print(iter_f.__next__())
迭代器取值的特点:
- 只能往后依次取,不能往回取值
常规错误点:避免出现print(d.iter().next()),因为它每次都重新开始一段新的迭代
for循环的本质
常规:
d = {'name':'jason','password':123,'hobby':'XXX'} for i in d: print(i)
试试能否将l迭代:
for i in l: pass iter(l)
注意点:for 循环后面的in, 跟的必须是一个可迭代对象,否则会报错------object is not iterable
for 循环内部的本质:
- 1.将in后面的对象调用__iter__转换成迭代器对象
- 2.调用__next__迭代取值
- 3.内部有异常捕获StopIteration,当用__next__取值时报这个错,则自动结束循环
l1 = [1,2,3,4] l2 = ['a','b','c','d'] print(zip(l1,l2)) # 这其实就是一个迭代器------返回结果为<zip object at 内存地址>
生成器:
定义:用户自定义的迭代器,本质就是迭代器
def fun(): print('first') yield # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 func() # 运行结果为空,但是能够初始化生成器,将函数变成迭代器
如果加上g = func():
def fun(): print('first') yield g = func() g() # 这时候就能够执行函数体代码了
这时候用__next__取值:
def fun(): print('first') yield 1 # yield后面跟的值就是调用迭代器next后,你能够得到的值 g = func() print(g) # <generator object func at 内存地址> print(g.__next__()) # 返回结果是 1
yield返回多个值的例子:
def fun(): print('first') yield 666,777,888 # yield既可以返回一个值也可以返回多个值,并且多个值也是按照元祖的形式返回 g = func() print(g.__next__()) # 返回结果是(666,777,888)
yield有暂停执行的作用:
def fun(): print('first') yield 1 print('second') yield 2 print('third') yield 3 yield yield g = func() print(g.next()) # 返回结果是 first 1 print(g.next()) # 返回结果是 first 1 second 2 print(g.next()) # 返回结果是 first 1 second 2 third 3 print(g.next()) # 返回结果是 first 1 second 2 third 3 None print(g.next()) # 返回结果是 first 1 second 2 third 3 None None
为 for i in range(1,10,1):
print(i) 创建一个自定义生成器
def my_range(start,end,step=2): while start < end: yield start start += step for j in my_range(1,10,2): print(j)
yield表达式形式:
注意点:yield支持外界为其传参
def dog(name) print('%s 准备开吃'%name) while True: food = yield print('%s 吃了 %s'%(name,food)) # 当函数内有yield关键字时,调用该函数不会执行函数体代码,而是将函数变成生成器 g = dog('egon') g.next() # egon 准备开吃 res = g.next() # 返回值为yield,且必须先将代码运行至yield,才能为其传值 g.send('狗不理包子') # 给yield左边的变量传参,触发了next方法 g.next() # egon 吃了狗不理包子
yield:
- 1.帮你提供了一种自定义生成器方式
- 2.帮你将函数的运行状态暂停住
- 3.可以返回值
与return之间异同点:
- 相同点:
- 都可以返回值,并且都可以返回多个值
- 不同点:
- yield可以返回多次值,而return只能返回一次函数立即结束
- yield还可以接收外部传入的值
生成器表达式:
res = [i for i in range(1,10) if i != 4] print(res) # [1,2,3,5,6,7,8,9]
res = (i for i in range(1,10) if i != 4) # 生成器表达式 print(res) # <generator ogject <genexpr> at 内存地址>,它本质上是一个生成器
print(res.__next__()) # 返回结果为1,再往后就依次返回2,3,5...
下面两种方式,前者太占用内存,而后者则是改进版本:
f = open('XXX.txt', 'r', encoding='utf-8') data = f.read() print(len(data)) f.close()
with open('XXX.txt', 'r', encoding='utf-8') as f: n = 0 for line in f: n += len(line) print(n)
在上述改进版本中,用迭代器的知识点来提高效率:
with open('XXX.txt', 'r', encoding='utf-8') as f: g = (len(line) for line in f) # 这行代码本质上是一个迭代器 print(sum(g)) # 将print(g.next())替换掉了
注意点:
- 生成器不会主动执行任何一行代码
- 必须通过__next__触发代码的运行
面试题目1(重要):
def add(n,i): return n + i def test(): for i in range(4): yield i g = test() for n in [1,10]: g = (add(n,i) for i in g) # 此段代码循环结束后,n = 10 # 第一次for循环,g = (add(n,i) for i in test()) # 第二次for循环,g = (add(n,i) for i in (add(n,i) for i in test())) res = list(g) # 返回值结果为[20,21,22,23] # 其中list(g),括号内部是基于for循环的 # 等价于for i in (add(n,i) for i in test()): # 得到 n = 10 后,再代入add(n,i)计算结果 # 另外补充一点, in后面跟生成器,就会触发 生成器
面试题目2(重要):
def multipliers(): return [lambda x: i*x for i in range(4)] print([m(2) for m in multipliers()]) # [6,6,6,6]
至于为何返回值不是[0,2,4,6],
原因:闭包函数的延迟绑定----在内层函数执行时才会绑定变量i
# 将上段代码用常用闭包函数的形式表达出来 def multipliers(): list1 = [] for i in range(4): def func(x): return x*i list1.append(func) return list1 print([m(2) for m in multipliers()]) # [6,6,6,6]
def multipliers(): list1 = [] for i in range(4): def func(x,i=i): # 第一个i是func函数的默认参数,第二个i是上面函数的i return x*i list1.append(func) return list1 print([m(2) for m in multipliers()]) # [0,2,4,6] # 这时就不存在闭包函数的延迟绑定了
面试题目3:
需求:读取一个文件并返回每行数据的长度
1.常规解决方法:
with open('test1.txt', 'w', encoding='utf-8') as f: for line in range(1000): f.write(f'www{line}aaa' * (line + 1) + ' ')
2.列表推导式(可处理数据量小的数据,若处理数据量大的文件会导致内存溢出)
res1 = [len(line) for line in open('test1.txt', 'r', encoding='utf-8')] print(res1)
3.生成器表达式:处理数据量大的文件推荐使用
res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8')) print(res2) # <generator object <genexpr> at 内存地址> print(next(res2)) # 开始返回值8 15 22 ...(依次加7)
面试题目4:
def demo(): for i in range(4): yield i g = demo() # <gen...>1 (0,1,2,3) g1 = (i for i in g) # <gen...>2 (0,1,2,3) g2 = (i for i in g1) # <gen...>3 print(list(g1)) # [0,1,2,3],这时候list()已经将所有值都获取出来了 print(list(g2)) # []
常用内置方法补充:
abs
print(abs(-11,11)) # 求绝对值
all,any
l = [0,1,2] print(all(l)) # False,----只要有一个为Flase就返回Flase print(any(l)) # True,----只要有一个为True就返回True
bin,oct,hex
print(bin(10)) # 0b1010------将10进制转化为二进制 print(oct(10)) # 0o12------将十进制转化为八进制 print(hex(10)) # 0xa------将十进制转化为十六进制
bool
print(bool(1)) # True print(bool(0)) # False
encode,bytes
s = 'hello' print(s.encode('utf-8')) # b'hello' print(bytes(s,encoding='utf-8')) # b'hello'
callable:可调用的(可以加括号执行相应功能的)
1 = [1,2,3] def index(): pass print(callable(l)) # False print(callable(index)) # True
chr:将数字转换成ASCLL码表对应的字符
# 将数字转换成ASCLL码表对应的字符 print(chr(65)) # A print(chr(97)) # a
ord:
# 将字符按照ASCLL转换为对应的数字 print(ord('a')) # 97
面向对象需要学习的方法:
classmethod delattr getattr hasattr issubclass property repr setattr super staticmethod
dir: 获取当前对象名称空间里所有它支持的名字
l = [1,2,3] print(dir(l))
divmod: 分页器
print(divmod(100,11)) # (9,1) 9页余1 # 例子: total_num,more = divmod(101,10) if more: total_num += 1 print('总页数:',total_num) # 总页数:82页
enumerate :枚举
l = ['a','b'] for i,j in enumerate(1,l): print(i,j) # 1 a # 2 b
eval :只能识别简单的python代码,不支持逻辑代码
exec :能够帮你识别字符串中的代码,然后执行(包括逻辑类代码)
s = " " " print('hello baby~') print(1 + 2) " " " eval(s) # 'hello baby~' 3 # 或者 name = 'jason' s = " " " name " " " print(eval(s)) # 'jason'
format三种玩法
- {}占位
- {index}索引占位
- {name}指名道姓
globals,locals
print(globals()) # 无论在哪都是将当前所有全局空间变量名打印出来 print(locals()) # 将当前所在的局部空间变量名打印出来,当前语句在哪个位置,就会返回哪个位置所存储的所有的名字
help
def login(): " " " XXX :resturn: " " '' print(help(login)) # 显示注释
isinstance
n = 1 print(type(n)) print(isinstance(n,list)) # 推荐的统一用法 : 判断对象是否属于某个数据类型 print(type(n)) is int) # 以前的常规判断方法
pow
print(pow(10,3)) # 10的3次方
round:四舍五入
print(round(3.49)) # 3 print(round(3.51)) # 4
面向过程的编程
面向过程编程 : 类似于设计一条流水线
- 优点:
- 将复杂的问题流程化,从而简单化
- 缺点:
- 可扩展性比较差,一旦需要修改,整体都会收到影响
例子 : 注册功能的实现
1.获取用户输入:
def get_info(): while True: username = input('>>>:').strip() if username.isalpha(): '''判断字符串必须是字母''' print('不能包含数字') continue password = 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:continue user_type = d.get(choice) operate_data(username,password,user_type) break else: print('两次密码不一致')
2.处理用户信息:
def operate_data(username,password,user_type): res = '%s|%s|%s '%(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)
4.合成功能:
def register(): get _info() register()