函数篇:生成器对象、自定义range方法、模块概念及导入
2022.3.24学习笔记
- 生成器对象(自定义迭代器)
- 自定义range方法
- 模块
一、生成器对象
1、概念与结构
概念:本质还是迭代器,只不过是通过自己写代码产生,也有双下iter和next方法,也是为了节省存储空间的
结构:
def index():
print('666')
yield 123 # yield和return相似,后面跟返回值,可以跟多个
res = index() # 相当于变身为生成器
res.__next__() # 调用一次生成器,执行生成器代码,遇到yield就结束
生成器就是在结构上是函数然后使用yield关键字,调用方式和迭代器一致,而且生成器与函数相似,但是不能直接调用,需要使用迭代器调用方法,并且yield与return也有区别,如下:
def index():
print('666')
yield 123
print('777')
yield 456
print('888')
yield 777
注意:使用双下next方法调用一次,相当于函数index运行到yield就结束,在调用一次next,再运行一个到yield截至,直到没有yield为止再取就会报错,并且yield可以不跟返回值,默认返回None
2、yield关键字作用
(1)在函数体代码中出现,可以将函数变成生成器
(2)在执行过程中可以将yield后面的值返回去,类似于return,但是不能用变量名接收返回值,只能调用
(3)可以暂停代码的运行,调用一次next方法只能运行到一次yield
(4)还可以接收外界的传值(了解)
# 接收外部传值举例
def eat(name):
print(f'{name}准备干饭')
while True: # 目的是循环yield,获取多个
food = yield # 用变量名接收,默认为None,但是yield可以接收外部传值
print(f'{name}正在吃{food})
res = eat('jason') # 变身生成器,并传参'jason'给name
res.send('蛋糕') # send可以给内部yield传值,send必须是在生成器的状态下调用,相当于是给yield传值并且调用一次双下next方法
3、生成器表达式
联想之前的列表字典生成式,为什么没有元组生成式,因为这里表示的是生成器表达式,如下:
res = (i for i in 'jason') # res就是生成器对象
print(res) # <generator object <genexpr> at 0x1130cf468>
print(res.__next__()) # j
注意:生成器内部的代码只有在调用__next__迭代取值的时候才会执行!!
4、生成器面试题!!!
def add(n, i):
return n + i
def test():
for i in range(4):
yield i
g = test() # 将test函数变成生成器对象
for n in [1, 10]:
g = (add(n, i) for i in g)
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是多少即可
"""
第一次for循环
g = (add(n, i) for i in g)
第二次for循环
g = (add(10, i) for i in (add(10, i) for i in g))
这时候还没进行迭代取值g后面的都可以不用看,这就是这道题的关键
"""
二、自定义range方法
range方法其实也是一个可迭代对象,所以是否可以借助生成器对象模拟出range呢?试一试吧
def my_range(start,end = 0,step=1):
if not end: # end未传参,则执行下面代码
end = start # 重新赋值,保证有头有尾
start = 0
while start < end:
yield start # 生成器
start += step # 控制步长,默认参数为1
这样,range方法就被模拟出来了
三、模块
1、模块简介
(1)什么是模块?
模块是一系列功能的结合体,可以直接使用
(2)为什么要用模块?
极大提升开发效率(使用别人写好的模块当然快啦)
(3)模块的三种来源
1.# 内置模块
无需下载,解释器自带,直接导入即可使用
2.# 自定义模块
自己写的代码,封装成模块,自用或者他用都可
3.# 第三方模块
别人写的代码发到网上的,可直接下载使用的模块
(4)模块的四种表现形式
1.使用python代码编写的py文件 # 掌握
2.多个py文件组成的文件夹(包) # 掌握
3.已被编译为共享库或DLL的c或C++扩展(了解)
4.使用C编写并链接到python解释器的内置模块(了解)
2、模块的两种导入方式
方式一:import md
import md # md是py文件名
print(md.name) # 调用md.py文件下的name变量名
注意:
(1)当前文件进行调用md,当前文件是执行文件,md是被导入文件
(2)内部原理:
执行当前文件产生一个当前文件的名称空间
使用import句式导入模块文件
当前文件名称空间产生一个模块的名字执行导入文件的名称空间
通过该名字点的方式就可使用被导入模块中的数据
(3)若重复导入同一个文件,效果是一样的
(4)两者文件有同样的名字,不冲突,优先调用本名称空间的变量名
方式二:from md import name
import后面可以跟多个变量名或者函数名等,调用多个进行使用
from md import name,money,read1 # 调用多个也可以,不写的不能使用
print(name) # jasonNB
name = 'kevin'
print(name) # kevin name在当前文件有则使用当前文件
3、导入补充
(1)可以给模块取别名
目的是为了有时候import后面的模块名或变量名较复杂的时候取个简单的别名
import md as 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.py中所有的名字
同时,另一方面在模块文件用可以使用__all__限制可以使用的名字,这时候星号就会失效,依据__all__后面列举的名字
__all__ = [name, age]
作业
1.利用比较好看的画图软件诠释出
两种模块导入方式的内部内部 图文相结合
2.整理今日内容博客、录音、单词
3.尝试将之前的周末大作业的多个函数拆分到不同文件中以模块导入的形式调用执行
1.两种模块导入方式内部原理图