python之生成器与模块


目录
  • 生成器对象
  • 自定义range方法
  • 生成器表达式
  • 模块
    • 简介
    • 模块的导入方式
      • 第一种:import ...
      • 第二种:from ... import ...
      • 补充

生成器对象

生成器对象其实本质还是迭代器,只不过这个迭代器的内容可以由我们直接来定义了,所以它也可以称为自定义迭代器。

先来看一段代码:

def index():
    print('abc')
    yield
print(index())  # 输出:

可以看到,在加了关键字yield之后,函数变成了生成器对象了。如果想要执行函数里的代码,就需要使用__next__()方法。

def index():
    print('abc')
    yield
res = index()
res.__next__()  # 输出:abc

多个yield

def index():
    print('abc')
    yield
    print('def')
    yield
    print('gh')
    yield
res = index()
res.__next__()  # 输出:abc
res.__next__()  # 输出:def
res.__next__()  # 输出:gh

在yield后也可以跟上一个值

def index():
    print('abc')
    yield 123
    print('def')
    yield 456
res = index()
r1 = res.__next__()  # 输出:abc
print(r)  # 输出:123
r2 = res.__next__()  # 输出:def
print(r)  # 输出:456

结论:

  1. 在函数内部添加了yield关键字之后,函数就会变成生成器对象,并且可以调用__next__方法。
  2. 当有多个yield关键字时,每执行一次__next__方法时函数代码会运行到下个yield处并停留。
  3. 如果yield后跟上了一个值,则执行__next__方法时会返回yield后的值。

补充:yield关键字的作用

除了以上的作用外,yield还可以接收外界的传值

def eat(name):
    print(f'{name}准备干饭')
    while True:
        food = yield
        print(f'{name}正在吃{food}')
res = eat('jason')
# 使用send方法前需要调用一次__next__启动
res.__next__()  # 输出:jason准备干饭
# 使用send方法给yield传值
res.send('kfc')  # 输出:jason正在吃kfc
res.send('米饭')  # 输出:jason正在吃米饭

自定义range方法

在了解完生成器对象后,这时候我们就可以自己定义一个和range一样功能的方法了。

先实现两个参数的情况:

def my_range(start, end):
    """
    start: 起步位置
    end: 结束位置
    """
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加1
        start += 1
for i in my_range(1, 4):
    print(i)

实现了两个参数后,我们可以开始解决一个参数的情况

def my_range(start, end=None):
    """
    start: 起步位置
    end: 结束位置,默认为空值
    """
    # 如果没有给end传值,说明只有一个参数,取值范围为[0,start)
    if not end:  
        end = start
        start = 0
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加1
        start += 1
for i in my_range(1, 4):
    print(i)

最后解决三个参数的情况

def my_range(start, end=None, step=1):
    """
    start: 起步位置
    end: 结束位置,默认为空值
    step:步长,默认为1
    """
    # 如果没有给end传值,说明只有一个参数,取值范围为[0,start)
    if not end:  
        end = start
        start = 0
    # 如果start一直累加后值等于end则退出循环
    while start < end:
        yield start
        # 每被执行一次__next__方法起始位置加步长
        start += step
for i in my_range(1, 4):
    print(i)

生成器表达式

直入主题,生成器结构

res = (i for i in 'jason')
print(res)  # 输出: at 0x000002AC49CF0200>
"""可以看到,它并不会像列表生成式一样直接输出"""
"""因为生成器内部的代码只有在调用__next__迭代取值的时候才会执行"""
print(res.__next__())  # 输出:j
print(res.__next__())  # 输出:a

模块

简介

模块就是一系列功能的结合体,只需要导入就可以直接使用,它极大的提升了开发效率。比如我自己要实现一个功能可能要写好几行代码,但是如果模块里可以实现这个功能,那我可以直接拿来使用。

模块的三种来源方式:

  1. 内置的模块

    这些是python自带的模块,直接用代码导入就可以使用。

  2. 自定义模块

    这些是自己写的代码封装成模块,可以自己使用或发布到网上

  3. 第三方模块

    这些是由别人发布到网上的,我们可以下载过来使用

模块的四种表现形式:

  1. 使用python代码编写的py文件
  2. 多个py文件组成的文件夹,也可以称为包
  3. 已被编译为共享库或DLL的c或C++扩展(了解)
  4. 使用C编写并链接到python解释器的内置模块(了解)

模块的导入方式

第一种:import ...

语法结构:

import 模块1, 模块2, ...

这种导入方式可以使用模块内部的变量和方法,一个模块只会被导入一次,不管你执行了多少次import。

案例一:

创建一个md.py文件

name = '来自md.py'
print(name)

创建一个main.py文件

import md

执行main.py文件,此时会输出

来自md.py

说明在导模块时,会执行被导入模块的内部代码。

案例二:

创建一个md.py文件

name = '来自md.py的name变量'
def index():
    print('来自md.py的index函数')

创建一个main.py文件

import md
name = '来自main.py的name变量'
print(name)
print(md.name)
def index():
    print('来自main.py的index函数')
index()
md.index()

执行main.py文件,此时会输出

来自main.py的name变量
来自md.py的name变量
来自main.py的index函数
来自md.py的index函数

说明导入模块后,想要模块内部变量和函数需要使用模块加点的方式,直接使用只会从当前文件寻找。

图解:

第二种:from ... import ...

语法结构:

from 模块名 import 名称1,名称2,...

这种方式使用模块内部的名称可以不需要用模块加点的方式了,可以直接使用。

案例一:

创建一个md.py文件

name = '来自md.py'
print(name)

创建一个main.py文件

from md import name

执行main.py文件,此时会输出

来自md.py

说明在导模块时,会执行被导入模块的内部代码。

案例二:

创建一个md.py文件

name = '来自md.py'
def index():
    print('来自md.py的index函数')

创建一个main.py文件

from md import name, index
print(name)
index()

执行main.py文件,此时会输出

来自md.py
来自md.py的index函数

说明使用from + import方法导入模块后,可以直接使用模块内部的名称,所以这时候就需要避免出现重名的情况。

图解

补充

  1. 导入模块时可以给模块起别名
import time as t
t.sleep(3)  # 等价于time.sleep(3)

from md import name as n, index as x
print(n)  # 等价于print(name)
x()  # 等价于index()
  1. 导入全部名称
from md import *
"""如果模块文件中使用了__all__限制可以使用的名字,那么*号就会失效,只能使用__all__后面列举的名字"""
# 在md.py文件中
__all__ = ['name']
# 这时如果使用from md import *只能使用name变量
  1. 建议

导入个模块时,可以把有相似功能的模块使用同一个import导入,如果没有建议分开导入。