前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python迭代器-迭代器取值-for循环-生成器-yield-生成器表达式-常用内置方法-面向过程编程-05

python迭代器-迭代器取值-for循环-生成器-yield-生成器表达式-常用内置方法-面向过程编程-05

作者头像
suwanbin
发布2019-09-26 11:31:26
1.4K0
发布2019-09-26 11:31:26
举报

迭代器

迭代器

迭代: # 更新换代(其实也是重复)的过程,每一次的迭代都必须基于上一次的结果(上一次与这一次之间必须是有关系的)

迭代器: # 迭代取值的工具

为什么用迭代器: # 迭代器提供了一种可以不依赖索引取值的方式

代码语言:javascript
复制
# 会一直打印0,记得停止
# n = 0
# while True:
#     print(n)

# 重复 + 每次迭代都是基于上一次的结果而来的
l = [1, 2, 3, 4, 5]
s = 'hello'
n = 0
while n < len(s):
    print(s[n])
    n += 1
# h
# e
# l
# l
# o

可迭代对象

代码语言:javascript
复制
'''
可迭代对象
    只要内置有 __iter__ 方法的都叫做可迭代对象(读:双下iter)
        补充: 针对双下划线开头结尾的方法,推荐读“双下 + 方法名”
    基本数据类型中,是可迭代对象的有(str, list, tuple, dict, set), 文件对象(执行 __iter__ 之后还是本身,__next__之后还是他本身)

    可迭代对象执行内置的 __iter__ 方法,得到的就是该对象的迭代器对象
'''
代码语言:javascript
复制
n = 1
f = 1.1
s = '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__方法得到就是该对象的迭代器对象

迭代器对象与可迭代对象的区别

代码语言:javascript
复制
'''
迭代器对象和可迭代对象有什么共同点
    迭代器对象
        1.内置有双下iter( __iter__) 方法
        2.内置有 __next__ 方法
        ps:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
'''

__iter__转换迭代器对象

代码语言:javascript
复制
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__())  # 取完了 报错StopIteration

f1 = 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循环原理

异常处理

代码语言:javascript
复制
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
# 老母猪生不动了

迭代器与迭代器对象总结

代码语言:javascript
复制
'''
可迭代对象:内置有 __iter__ 方法
迭代器对象:既内置有__iter__ 方法,也内置有__next__方法

迭代取值的优点与缺点:
    优点:
        不依赖于索引取值
        内存中永远只占一份空间,不会导致内存溢出
    缺点:
        不能获取指定的元素
        取完之后会报StopIteration错..
'''

for循环内部原理

代码语言:javascript
复制
'''
for 循环内部的本质
    1.将in后面的对象调用 __iter__ 转换成迭代器对象   --> 所以前面的疑问,文件对象本来就是迭代器对象还有 __iter__ 方法
    2.调用__next__ 迭代取值
    3.内部有异常捕获,StopIteration,当__next__报这个错,自动结束循环
'''

 内部原理同上面异常处理里的案例

生成器

生成器: # 用户自定义的迭代器,本质就是迭代器

注意点: # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 , # yield后面跟的值就是调用迭代器__next__方法你能得到的值 , # yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回

 yield 返回值(体现上面的注意点)

代码语言:javascript
复制
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
# None

print(g.__next__())
# second
# 2

print(g.__next__())
# third
# (3, 4, 5)

小案例

代码语言:javascript
复制
# 自定义range 功能
for i in range(10):
    print(i, end='- ')
print()


def my_range(start, end, step=1):  # 只传一个参数的,range,判断参数个数  搜:python只传一个参数的range
    while start < end:
        yield start
        start += step


for i in my_range(1, 10, 3):
    print(i)
# 1
# 4
# 7

yield表达式形式传入参数

代码语言:javascript
复制
# yield 表达式形式(传参的第三种方式,直接传参,闭包)
def dog(name):
    print("%s 准备开始吃" %name)
    while True:
        food = yield
        print("%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 吃了 None
g.send('饺子')
# egon 吃了 饺子

总结yield并与return作对比

代码语言:javascript
复制
'''
总结yield:
    1.yield 提供了一种自定义生成器的方式
    2.yield 会帮你将函数的运行状态暂停住
    3.yield 可以返回值
    
与return 之间的异同点:
    相同点:
        1.都可以返回值,并且都可以返回多个,返回多个都是包装成元组
    不同点:
        1.yield 可以返回多次值,而return  只能返回一次函数立即结束
        2.yield还可以接收外部传入的值
    
'''

常用内置方法

常见数学内置函数 abs 绝对值      pow 求平方    round  四舍五入   divmod 将运算结果除数与余数存入元组

代码语言:javascript
复制
# 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 = 101
counts_each_page = 5
total_pages, more = divmod(all_count, counts_each_page)
if more:
    total_pages += more
print(total_pages)
# 21  --> 共101条内容,按每页5条来分,需要分21页

数据类型进制转换  isinstance(判断数据类型)  all(传过来容器类型必须全部都是True才返回True)   any(有一个是True就返回True)   bool(将传进来参数变成布尔类型)   bin(十转二) oct(十转八)  hex(十转十六)    int(把指定类型转成十进制)

代码语言:javascript
复制
# isinstance 判断数据类型,后面统一用该方法判断对象是否属于某一个数据类型
n = 1
print(type(n))
print(isinstance(n, list))  # 判断对象是否属于某个数据类型
# <class 'int'>
# False

# all 传过来的容器类型里必须全部是True,否则返回False
# any 只要一个True,就返回True
l = [0, 1, 2]
print(all(l))  # 只要有一个是False,就返回False
print(any(l))
# False
# True

# bool
print(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

字符编码  ascii (个人觉得没啥用)  chr(将数字转换成ascii码 对应的字符)    ord (将字符按照ascii码 转成数字)  bytes(转成二进制)

代码语言:javascript
复制
# ascii  个人觉得没啥用
print(ascii('a'))
# 'a'
print(ascii(65))
# 65
print(ascii('c'))
# 'c'

# chr  ord 将数字转换成ascii 码表对应的字符  将字符按照ascii 码表转成对应的数字
print(chr(65))
# A
print(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'

可迭代对象常见内置函数  slice  切片(生成的是老母猪,节省空间)  enumerate  给可迭代对象生成索引(假索引)

代码语言:javascript
复制
# 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

执行字符串中的python语句以及python字符串格式化  eval (执行字符串中的python语句,不够强大)   exec (执行python中的语句,能执行逻辑的)   format(格式化字符串)

代码语言:javascript
复制
# 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.

对象命名空间 callable(对象是否可调用)  dir (获取该对象名称空间里的名字)  globals (查看全局名称空间的名字)    locals (返回当前位置名称空间的名字)  help (查看函数的注释)

代码语言:javascript
复制
# callable 可调用的,可以加括号执行相应功能
def index():
    pass
l = [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

面向过程编程

概述: # 面向过程就类似于流水线设置,将复杂问题分步实现

优点: # 可以将复杂的问题流程化,从而简单化

缺点: # 可扩展性差,如有改动,可能会涉及超大面积更改

小案例(牵一发动全身)

代码语言:javascript
复制
# 获取用户登录
def get_info():
    while True:
        username = input(">>>:").strip()
        if not username.isalpha():  # 判断字符串不能包含数字
            print('不能包含数字')
            continue
        password = input('>>>:').strip()
        confirm_password = input("confirm>>>:").strip()
        if password == confirm_password:
            operate_data(username,password)
            break
        else:
            print('两次密码不一致')

# 2.处理用户信息
def operate_data(username,password):
    # jason|123
    res = '%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()
代码语言:javascript
复制
# 在用户注册时候让用户选择用户类型
# 1.获取用户输入
def get_info():
    while True:
        username = input(">>>:").strip()
        if not 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):
    # 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 是什么

代码语言:javascript
复制
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)
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]
代码语言:javascript
复制
# C. res = [20, 21, 22, 23]

2.请判断下面程序会输出什么

代码语言:javascript
复制
# 已经提前把里面的东西取出来了

3.利用生成器表达式读取文件行数

代码语言:javascript
复制
'''
需求: 读取一个文件并返回每行数据的长度
'''
with open('test1.txt', 'w', encoding='utf-8') as f:
    for line in range(1000):
        f.write(f'www{line}aaa' * (line + 1) + '\n')
代码语言:javascript
复制
# 列表推导式: 处理数据量大的文件会导致内存溢出.
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.写出下面代码的执行结果

代码语言:javascript
复制
# 请写出下面代码的执行结果
def mutipliers():
    return [lambda x: i*x for i in range(4)]


print([m(2) for m in mutipliers()])
代码语言:javascript
复制
# [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]

# 闭包函数的延迟绑定
# 在内层函数执行时才会绑定变量i
def multipliers2():
    list1 = []
    for i in range(4):

        def func(x, i=i):

            return x * i

        list1.append(func)

    return list1

print([m(2) for m in multipliers2()])  # [0, 2, 4, 6]

# [0, 2, 4, 6]
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-07-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 迭代器
    • 可迭代对象
      • 迭代器对象与可迭代对象的区别
        • __iter__转换迭代器对象
          • 异常处理
            • 迭代器与迭代器对象总结
              • for循环内部原理
                •  yield 返回值(体现上面的注意点)
                • yield表达式形式传入参数
                • 总结yield并与return作对比
            • 生成器
            • 常用内置方法
            • 面向过程编程
            •  扩展练习题(面试题)
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档