生成器/range/yield/模块

发布时间 2023-07-27 23:36:34作者: 半糖+奶茶

生成器对象(自定义迭代器)

 

本质其实还是迭代器 只不过是我们自己通过写代码产生
也是有__iter__和__next__方法

 

def index():
     print('你还记得我吗?')
    yield 123
     
'''生成器对象也是节省存储空间的 特性与迭代器对象一致'''
"""
当函数体代码中含有yield关键字
    第一次调用函数并不会执行函数体代码
    而是将函数变成了生成器
"""

#没有调用之前 就是一个普通的函数
 print(index)  # <function index at 0x1096c0ea0>

# 加括号调用并接收结果:不执行代码 而是变成生成器对象(迭代器)
res = index()


 print(res)  # <generator object index at 0x11da33468>
# 变成生成器对象之后调用__next__就会开始执行函数体代码

 print(res.__next__())  #返回 123  yield有点像return的功能
def index():
     print('你还记得我吗?')
    yield 123
     yield 123, 111
     print('是不是忘记我了!!!')
    yield 666

res = index() #res此时变成生成器对象(只有函数被调用才能变成生成器对象,但是不执行代码)

print(res.__next__())  # 123  yield有点像return的功能
 print(res.__next__())  # (123, 111)  
 print(res.__next__())  # 666  
 print(res.__next__())  # 依次往后,直到取值为空,最后报错StopIteration


'''相当于for循环中的'''
for i in res:
    print(i)

 自定义range方法

 

range方法其实是一个可迭代对象
 for i in range(1, 10):
     print(i)

'''需求:通过生成器模拟range方法(课堂练习)'''


# ①先以两个参数的range方法为例
def my_range(start, end):  #start表示起始值,end表示结束值,注意真正的range最后一个数字不打印
        while start < end:  #循环的结束条件
        yield start   #返回start值
        start += 1  

 for i in my_range(1,100):
     print(i)  #打印了从1到99  可以实现

 

'''初步功能实现之后 再去考虑不同参数的情况 一个参数 三个参数'''
'''一个参数代表end的值可以没有,三个参数代表最后一个可以作为步长,根据这个考虑对range进一步优化'''

def my_range(start, end=None, step=1):  #end值变为None,可以不传值,step表示默认不传值为1,若传值用传值的步长
    if not end:  # 没有给end传值  比如my_range(10)
        end = start    #形参数据做替换
        start = 0        #形参数据做替换
    while start < end:
        yield start
        start += step   #原来的1变为现在的step,更加灵活   
 for i in my_range(1,100):
     print(i)

'''完成搭建!!!'''

yield关键字作用

 1.在函数体代码中出现 可以将函数变成生成器
 2.在执行过程中 可以将后面的值返回出去 类似于return
 3.还可以暂停住代码的运行
 4.还可以接收外界的传值(了解)

def eat(name):
    print(f'{name}准备干饭')
    while True:
        food = yield   #外界传递给yield值用send
        print(f'{name}正在吃{food}')
res = eat('赵公子')  #res现在是变为迭代器对象
# 想执行一次代码  如果想执行多次直至结束 可以直接用for循环
res.__next__()   #赵公子准备干饭(第一节调用到yield结束)
res.__next__()  赵公子正在吃None

res.send('生日蛋糕')  # 可以给yield传值 并且自动调用一次__next__方法
res.send('大鸡腿')  # 可以给yield传值 并且自动调用一次__next__方法
#分别得到赵公子正在吃生日蛋糕和赵公子正在吃大鸡腿

生成器表达式

也是为了节省空间(在后期做代码优化的时候,可以考虑使用,前期学习阶段可以忽略)

 res = (i for i in 'jason')   #这个是元组变成迭代器的写法和上面yield可以看成差不多的作用
 print(res)  # <generator object <genexpr> at 0x1130cf468>实现变成生成器
# print(res.__next__())   #后期调用也是同样方法!
"""生成器内部的代码只有在调用__next__迭代取值的时候才会执行"""
之前的笔试题目(难度较大,了解即可,不做掌握)
# 普通的求和函数
def add(n, i):
    return n + i
# 生成器对象 返回 0 1 2 3
def test():
    for i in range(4):
        yield i   #有yield函数后面进行调用即可变成生成器对象
# 将test函数变成生成器对象
g = test()
# 简单的for循环
for n in [1, 10]:   #n只有两值循环,注意这个是列表,一个是1一个是10
    g = (add(n, i) for i in g)
    """
    第一次for循环
        g = (add(n, i) for i in g)  #函数变成生成器对象(元组)
    第二次for循环
        g = (add(10, i) for i in (add(10, i) for i in g))  #底层是10加10
    """
print(n)  #在外界打印n值是10,取最后的值,如果实在for n in [1, 10]:条件里面打印会有两个值
res = list(g)  # list底层就是for循环 相当于对g做了迭代取值操作
print(res)

#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  诀窍就是抓n是多少即可"""

 模块

 

python之所以很火并且适用于各行各业很大程度上的原因就是>>>:模块
很多大佬写了很多非常牛逼的模块 供python工程师直接使用(白嫖)

温馨提示:作为一名python工程师 如果遇到一个非常复杂的功能需要实现 那么第一时间不是想着如何去写 而是去网上找有没有相应的python模块!!!
以后出去工作了之后才应该考虑的
eg:人脸识别 语言转换 自动感应 类似更本摸不着头脑的几乎都有模块

1.什么是模块?
    模块就是一系列功能的结合体 可以直接使用
2.为什么要用模块?
    极大地提升开发效率(拿来主义>>>:站在巨人的肩膀上)
3.模块的三种来源
  (1).内置的模块
      无需下载 解释器自带  直接导入使用即可
  (2).自定义模块
      自己写的代码 封装成模块 自己用或者发布到网上供别人使用
   (3).第三方模块
      别人写的发布到网上的 可以下载使用的模块(很多牛逼的模块都是第三方)
4.模块的四种表现形式(大白话:模块长啥样子)
  1.使用python代码编写的py文件         # 掌握
  2.多个py文件组成的文件夹(包)     # 掌握
  3.已被编译为共享库或DLL的c或C++扩展(了解)
  4.使用C编写并链接到python解释器的内置模块(了解)

模块的两种导入方式

"""要想使用模块 必须先导入 而导入的方法有两种"""
 方式1>>>:import...句式

方式2>>>:from...import...句式

前提:在研究模块的时候 一定要分清楚谁是执行文件 谁是被导入文件(模块)
    模块简介.py(我们现在写的)是执行文件  md.py是被导入文件(里面的东西都已经被人写好了,直接拿出来用就可以了)

"""
导入模块内部到底发送了什么事情
    1.执行当前文件 产生一个当前文件的名称空间
    2.执行import句式 导入模块文件(即执行模块文件代码产生模块文件的名称空间)
    3.在当前文件的名称空间中产生一个模块的名字 指向模块的名称空间 
    4.通过该名字就可以使用到模块名称空间中的所有数据
ps:相同的模块反复被导入只会执行一次
    import md   有效
    import md   无效(写了跟没写一样)
    import md   无效(写了跟没写一样)
"""
"""
import句式的特点
    可以通过import后面的模块名点的方式 使用模块中所有的名字
    并且不会与当前名称空间中的名字冲突(指名道姓)
   当前名称空间与模块中名称空间就算名字一样,也互不干扰,调用模块名称空间的名字必须要用模块名加点调用
"""

name = 'kevin'
 print(md.name)  # jasonNB  获取模块名称空间中的name
 print(name)  # kevin  获取当前名称空间中的name
#from...import...句式

from md import name   #调用模块名称空间里面的name,但是不调用其他的话,其他则不能用
 print(name)  # jasonNB  (模块名称空间里面的name)
 name = 'kevin'   #现在文件文件模块的name
 print(name)  # kevin  打印出来是现在的,所有其实会导致刚开始name指向的是模块空间里面的name,现在指向的是本文件中重新定义的name,发生混乱,而且之前的name回不来了!!
print(money)  # 报错  from md import name 只使用模块中的name名字,并没有调用模块空间中的money


"""
1.执行当前文件产生一个名称空间
2.执行导入语句 运行模块文件产生名称空间存放运行过程中的所有名字
3.将import后面的名字直接拿到当前执行文件中
"""
"""
1.重复导入也只会导入一次
2.使用模块名称空间中的名字不需要加模块名前缀 直接使用即可
3.但是from...import的句式会产生名字冲突的问题
    在使用的时候 一定要避免名字冲突
4.使用from...import的句式 只能使用import后面出现的名字
    from...import...可以简单的翻译成中文
        从...里面拿...来用 没有提到的都不能用      指名道姓
"""

导入补充

#1.可以给模块起别名(使用频率很高)
'''比如模块名或者变量名很复杂 可以起别名简写'''import md as m(这里的m就是别名)
 print(m.name)


 ②from md import name as n
 print(n)

# 2.连续导入多个模块或者变量名import time, sys, md

② from md import name, read1, read2  (如果是同一个模块下的多个变量名无所谓!!!)
"""连续导入多个模块 这多个模块最好有相似的功能部分 如果没有建议分开导入
"""
 import time
 import sys
 import md

# 3.通用导入
from md import *
'''*表示md里面所有的名字   from...import的句式也可以导入所有的名字
如果模块文件中使用了__all__限制可以使用的名字  那么*号就会失效 依据__all__后面列举的名字
'''