python06-文件操作(open 函数、os 模块)、 try...except、生成器、迭代器和可迭代对象

一、文件操作

 

使用场景:上传文件  保存日志

 1、open 函数

使用格式参数:

def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): # known special case of open

file :可以是文件名字,也可以是文件路径,可以是字节文件(图片)、可以是文本文件(记事本),写文件名,必须跟.py 执行文件同级目录中,或者使用相对路径,其余的都需要整体得到文件路径,open必须是文件,不可以是文件夹

mode:模式,是要读取还是写入,所以参数可以是   r 只能读纯文本文件 、w 只能写纯文本文件、 rt 默认不写mode,代表mode=rt 只能读,其中t 是 test mode 、 rb  b代表二进制文件,比如图片,所以rb读取字节文件,并且可以读文本文件,底层都是二进制,是万能,但不推荐文本,文本显示二进制,人无法识别,所以还需要转换成人能识别的,而图片等py不支持转换人能识别的、wb 写入字节文件,a  是追加内容,默认 w 、wb 是直接将源文件覆盖,而 a 是在源文件追加,+ 打开一个硬盘文件,用于更新

buffering: 可选整型参数,用于缓存,缓存主要在内存,调节硬件跟cpu之间的缓冲,比如要上传很大的文件,需要使用此参数,让先存入缓存,否则直接给cpu,系统就会由于cpu占导致系统崩溃。并不是越大越好,会影响系统的性能

encoding: 编码,默认py使用 utf-8,但比如有一些文件需要gbk格式才能打开,需要指定encoding = gbk 

2、读取文件

注意, \r : 回车, \n : 换行, \t : 制表符 \a 等,是系统的参数,前面加上r 转义

read ()  首先需要先使用open 打开这个文件,并设置open打开的用途是 r,通过open打开后是一个字符流,通过字符流就可操作,调用read函数,读取文件

stream = open(r'E:\code\pythonProject\ac.txt')  #  默认 mode = rt
a = stream.read()
print(a)

通过读取read 方法,可以看到使用了装饰器,说明是抽象方法,既然是抽象方法,就需要使用变量取接收,所以使用 a = stream.read()

注意,read 是读,如果文件不存在报错

读取word,会提示使用gbk

readable() 是不是可读,返回 bool 类型

readline()  读取一行

open() 打开一个文件,执行完成后需要执行clone() ,这样才能实现关闭open() ,而且注意,open() 打开一个文件后,在执行read() 或者readline() 读取后,第一次是从file文件的头读到尾部,如果是readline,执行一次读取一行,下此在执行后,会接着这次继续读

使用 readlin() 读取全部,需要用 for 循环,注意readline 按照行读,读取一次会打印一个\n

readlins() 读取所有的信息,并存入在列表中,而且每次执行一次readline都会打印一个\n

读取图片,不是模式用人的  mode = 'r'  

 

3、写文件

1、写文件,可以写已经存在的或者写不存在的,会创建
2、open 打开的时候 mode = w 或者 wb ,如果是 r rt  rb 则不能调 write 等方法,因为打开就是 r 不是为了写

3、注意mode = w 使用 write 或者 writeline 是覆盖原数据,只有mode= a 使用 append 是追加

stream = open(r'E:\code\pythonProject\write.txt', 'w')
s = """
你好
   世界
   送你一个花花
"""
result = stream.write(s)  # 执行 stream.write(s) 写入文件,返回结果是写入多少字符,注意换行符算一个
print(result)
stream.close()  # 注意别忘了关闭

 注意:write  写入的默认不会换行

stream = open(r'E:\code\pythonProject\write.txt', 'w')
s = """
你好
   世界
   送你一个花花
"""

a = "在写一行" 
b = "在写另一行"
stream.write(s)
stream.write(a)
stream.write(b)
stream.close()

如果要写入的是一个变量,使用

def register():
    username = input("输入用户名:")
    password = input("输入密码:")
    repassword = input("输入确认密码:")

    if username == 'admin' and password == '123456':
        with open(r'E:\code\dir1\book\user.txt','a') as wstream:
            wstream.write('{} {}\n'.format(username,password))

        print("用户注册成功")

writelines 一次性写入多行

stream = open(r'E:\code\pythonProject\write.txt', 'w')
stream.writelines(['在写一行', '在写另一行'])
stream.close()

使用\n 换行

stream = open(r'E:\code\pythonProject\write.txt', 'w')
stream.writelines(['在写一行\n', '在写另一行'])
stream.close()

4、写文件追加 mode = 'a'

stream = open(r'E:\code\pythonProject\write.txt', 'a')
a = "nihao"
stream.write(a)
stream.close()

5、文件复制

文件复制,先读取一个文件,在将这个文件写入到一个文件

了解一个模板:with open() as ,他的作用跟我们正常使用的一样,但不需要再写clone,自动帮助我们释放资源  。

stream 是 E:\code\pythonProject\write.txt 别名,通过stream.name 查看

截取E:\code\pythonProject\write.txt的write.txt,主要用于文件上传,你不知到源文件是什么,通过此方式获取文件名字

with open(r'E:\code\pythonProject\write.txt', 'rb') as stream:
    print(stream.name)
    file = stream.name  # E:\code\pythonProject\write.txt
    print(file.rfind('\\'))  # 通过右find找到最后一个\的index = 21
    print(file[file.rfind('\\')+1:]) # 使用截取,从下表 21+1开始,到最后

with open(r'E:\code\pythonProject\write.txt', 'rb') as stream:
    container = stream.read()  # 复制的前提,是先读
    print(container)

    with open(r'E:\code\pythonProject\copy\write.txt', 'wb') as wstream:  # 还得在写一个管道,因为open mode = rb 只能读,或者 open mode = wb 只能写
        wstream.write(container)  # 写入,写谁呢?container

在上述的案例中不能复制文件夹,open必须是文件

2、os 模块

1、os.path

os.path 打印 python 的 路径

os.path.dirname()  返回当前目录名字,而且是绝对路径

os.path.dirname(__file__)  # __file__  表示当前自己

a = os.path.dirname(__file__)
print(type(a))  # 这个路径是一个字符串
print(id(a))

os.path.join()  将路径拼接

path = 'E:\code\pythonProject'
path1 = 'copy\dir'
print(os.path.join(path, '123.txt'))
print(os.path.join(path, path1))

os 实现复制文件

import os.path

with open(r'E:\code\pythonProject\write.txt', 'rb') as stream:
    container = stream.read()

    path = os.path.dirname(__file__)
    path1 = os.path.join(path, 'a1.txt')
    with open(path1, 'wb') as wstream:
        wstream.write(container)

os.absolute 是不是绝对路径

import os.path

r = os.path.isabs(r'E:\code\pythonProject\write.txt')
print(r)  # True  绝对路径
r = os.path.isabs(r'pythonProject\write.txt')
print(r)  # False 相对路径

r = os.path.isabs(r'..\..\write.txt')  # ..\ 上一级目录
print(r)  # False

os.path.abspath 给我一个相对路径,返回绝对路径

获取当前文件的绝对路径,比如我使用123.py 执行的代码,打印123.py的绝对路径

import os.path

r = os.path.abspath(__file__)
print(r)

os.path.isfile 是一个文件么? os.path.isdir 是一个目录么?

r = os.path.isfile(os.getcwd())
print(r)  # False

r = os.path.isdir(os.getcwd())
print(r)  #True

os.path.split  将一个绝对路径的文件进行拆分成两个部分,一个是文件所在路径,一个是文件名,将这两个信息组成一个元组

path = r'E:\code\pythonProject\123.py'

result = os.path.split(path)
print(result)

获取文件名

方式一:

   

方式二:

os.path.splitext   将一个绝对路径的文件进行拆分成两个部分,在最后扩展名之前为一个,扩展名一个,将这两个信息组成一个元组,专门分割文件与扩展名

import os.path

path = r'E:\code\pythonProject\123.py'

result = os.path.splitext(path)
print(result)

os.path.getszie  获取文件大小,返回单位是字节个数

import os.path

path = r'E:\code\pythonProject\123.py'

size = os.path.getsize(path)
print(size)   # 107

os.path.exists  判断是否有此文件夹或者文件,反会bool

result = os.path.exists(r'E:\code\pythonProject\a1.txt')
print(result)  # True

2、os.下的函数

os.listdir  指定目录下的所有的文件和文件夹,返回一个列表

import os

path = r'E:\code\pythonProject'

result = os.listdir(path)
print(result) 

os.getcwd()  当前文件的文件夹,类似于os.path.dirname(__file__)

os.mkdir  创建目录

import os

result = os.mkdir(r'E:\code\p1')
print(result)  # 返回 None

文件夹已经存在会报错,所以最好创建之前需要判断,建议使用os.path.exists 

import os

if not os.path.exists(r'E:\code\dir'):
    result = os.mkdir(r'E:\code\dir')
print(result)

os.rmdir 删除文件夹,只能删除空目录文件夹。不存在目录会报错,所以最好删除之前需要判断,建议使用os.path.exists 。os.removedirs 功能一样

import os

result = os.rmdir(r'E:\code\p1')
print(result)   # 返回 None

注意 os.rmdir  os.removedirs  只能删除空目录

如何删除非空目录,个人觉得,傻的一笔

import os

path = r'E:\code\dir'

filelist = os.listdir(path)  #
print(filelist)

for file in filelist:
    path1 = os.path.join(path.file)
    os.remove(path)
else:
    os.rmdir(path)

print("删除成功")

os.chdir()  切换目录

import os

path = os.getcwd()
print(path)

f = os.chdir(r'E:\code')
print(f)
path = os.getcwd()
print(path)

3、复制文件夹

案例:复制文件夹中所有内容到另一个目录中

复制文件夹中所有内容,就是将文件夹中的内容复制到另一个目录中,所以是不是可以使用遍历文件夹中内容,然后一个一个复制到另一个文件夹中。注意到目前未知,我们不能对文件夹进行复制操作

import os

src = r'E:\code\dir1'
dest = r'E:\code\dir2'

def copy(src,dest):
    if os.path.isdir(src) and os.path.isdir(dest):
        filelist = os.listdir(src)
        print(filelist)

        for file in filelist:
            path = os.path.join(src,file)
            with open(path,'rb') as stream:
                container = stream.read()

                path1 = os.path.join(dest,file)
                with open(path1,'wb') as stream1:
                    stream1.write(container)
        else:
            print("复制完毕")

copy(src,dest)

 

上述中,添加一个文件夹验证,在执行,提示没权限

在上述代码中,添加判断,如果是目录进行操作1,不是目录则进行操作2。其中操作 2 不是目录的操作搭建完了,那就考虑是目录,思考如果是目录的话,是不是还是将这个目录中的文件一个一个在复制过去不就行了么,如果在遇到目录,还是将这个目录中的文件一个一个在复制过去不就行了么。·这就是我们的copy函数,典型函数的递归

import os

src = r'E:\code\dir1'
dest = r'E:\code\dir2'

def copy(src,dest):
    if os.path.isdir(src) and os.path.isdir(dest):
        filelist = os.listdir(src)

        for file in filelist:
            path = os.path.join(src,file)
            if os.path.isdir(path):  # 如果是目录的话,是不是可以进入到此目录中,然后在进行copy,那是不是,跟我们的copy函数的意义一样,
                                     # 将文件夹中的内容复制都另一个文件夹中,所以是不是可以是一个函数的递归
                dest = os.path.join(dest,file)
                if not os.path.isdir(dest):
                    os.mkdir(dest)
                copy(path,dest)

            else:  # 如果不是目录的话
                with open(path, 'rb') as stream:
                    container = stream.read()

                    path1 = os.path.join(dest, file)
                    with open(path1, 'wb') as stream1:
                        stream1.write(container)
        else:
            print("复制完毕")

copy(src,dest)

4、持久化保存

持久化保存,将信息放入文件中或者数据库中,不存在内存中

图书管理系统

# 用户注册
def register():
    username = input("输入用户名:")
    password = input("输入密码:")
    repassword = input("输入确认密码:")

    if password == repassword:
        with open(r'E:\code\dir1\book\user.txt', 'r') as rstream:
            container = rstream.readlines()
            # print(container)
            for i in range(0,len(container)):
                # print(container[i])
                a = container[i].find(' ')
                # print(a)
                # print(container[i][:a])
                if container[i][:a] == username:
                    print("用户已存在")
                    break
            else:
                with open(r'E:\code\dir1\book\user.txt', 'a') as wstream:
                    wstream.write('{} {}\n'.format(username, password))
                    print("用户注册成功")

    else:
        print("注册失败,密码不一致")

register()
# 用户登录
def login():
    username = input("输入用户名:")
    password = input("输入密码:")

    with open(r'E:\code\dir1\book\user.txt', 'r') as rstream:
        container = rstream.readlines()
        for i in range(0, len(container)):
            a = container[i].find(' ')
            if container[i][:a] == username:
                # print(container[i][a+1:])
                if container[i][a+1:-1] == password:
                    print("登录成功")
                else:
                    print("密码错误")
                break
        else:
            print("用户不存在")

# register()
login()

上述的验证用户名跟密码,读取文本成为readlines列表,在通过 for in 取每一个数据,然后通过空格切割,将用户名跟密码切割出来

还有一种,就是将账户和密码组合成  admin 123456 这种形式,对比readlines列表,在通过 for in 取每一个数据

def login():
    username = input("输入用户名:")
    password = input("输入密码:")

    with open(r'E:\code\dir1\book\user.txt', 'r') as rstream:
        while True:
            line = rstream.readline()
            input_user = '{} {}\n'.format(username, password)
            print(input_user)
            if line == input_user:
                print("登录成功")
                break
            else:
                print("登录失败")
                break
login()
# 浏览书
def show_books():
    print('-----展示图书----')
    with open(r'E:\code\dir1\book\books.txt','r') as restream:
        books = restream.readlines()
        print(books)
        for book in books:
           print(book,end="")


# register()
show_books()

二、异常机制 try...except

2.1、语法错误、异常

语法错误,如下,number是全局变量,必须通过 gloabl 才能在局部修改,所以报出红色波浪线,这种一般在运行时候,python 能检测语法,程序员可以查看后修改

 

异常:程序员编写时候无任何异常,无报出红色波浪线,但是在真正运行的时候出错,这种程序员不好判断。如下这种,报出红色波浪线,语法无问题,但是 0 不能作为被除数,数学逻辑不符。而且只要程序出错,出错之下的程序是不会在执行的

2.2、处理异常格式

格式:try...except....

1、可能出现异常的代码,存在 try 下。在程序运行期间,会执行 try 下代码,如果无任何异常,则不会执行 except.下代码
2、执行 try 下代码,如果此处异常出错,会执行 except.下代码,完成后续的代码
3、try...except.. 缺一不可

try:
    # 可能出现的异常代码
except:
    # 如果有异常执行的代码

格式:try...except....finally....

4、finally  可有可无,作用无论有没有异常,都会执行的代码

try:
    # 可能出现的异常代码
except:
    # 如果有异常执行的代码
finally:
    # 无论有没有异常,都会执行的代码

2.3、处理异常使用

如下,做一个两数相加的代码,会存在一个问题,如果输入的是一个字符串就会报错,在第一步int  就无法将字符串转换成整型

def func():
    n1 = int(input("请输入一个数字:"))
    n2 = int(input("请输入与第二个数字:"))
    sum = n1 + n2

    print("和是:", sum)

func()

将上述代码加 try except

def func():
    try:
        n1 = int(input("请输入一个数字:"))
        n2 = int(input("请输入与第二个数字:"))
        sum = n1 + n2

        print("和是:", sum)
    except:
        print("上面的格式有问题")

func()

思考:一个算数的代码,如果输入了非字符,通过 int 转换失败报错,会执行except,而如果执行除法,但被除数却输入了0,会执行except ,如何区分是 int 转换失败报错,执行了except 还是除法,被除数却输入了0,执行了except

def func():
    try:
        n1 = int(input("请输入一个数字:"))
        n2 = int(input("请输入与第二个数字:"))
        per = input("输入运算符号(= - * /):")
        num = ''
        if per == '+':
            result = n1 + n2
            num = '加法'
        elif per == '-':
            result = n1 - n2
            num = '减法'
        elif per == '*':
            result = n1 * n2
            num = '乘法'
        elif per == '/':
            result = n1 / n2
            num = '除法'
        else:
            print("符号输入错误")

        print('{}和{}{}结果是:{}'.format(n1, n2, num, result))
    except:
        print("上面的格式有问题")


func()

为了解决上述问题,通常需要报错的类型,区分返回那个except

int 转换失败报错 ValueError

 被除数却输入了0报错ZeroDivisionError

改写代码

def func():
    try:
        n1 = int(input("请输入一个数字:"))
        n2 = int(input("请输入与第二个数字:"))
        per = input("输入运算符号(= - * /):")
        num = ''
        if per == '+':
            result = n1 + n2
            num = '加法'
        elif per == '-':
            result = n1 - n2
            num = '减法'
        elif per == '*':
            result = n1 * n2
            num = '乘法'
        elif per == '/':
            result = n1 / n2
            num = '除法'
        else:
            print("符号输入错误")

        print('{}和{}{}结果是:{}'.format(n1, n2, num, result))
    except ValueError:
        print("上面的格式有问题")
    except ZeroDivisionError:
        print("被除数不能是0")
    except:
        print("其他异常问题")


func()

2.4、异常函数归纳

查看 ZeroDivisionError 函数

ZeroDivisionError 函数是子类,继承的 ArithmeticError 这个

 

 ArithmeticError函数是子类,继承的 Exception 这个

  Exception函数是子类,继承的 BaseException 这个

  BaseException 函数是子类,继承的 object 这个

object 类

 

查看 ValueError  函数

 ValueError  函数是子类,继承的 Exception 这个

总结:两个函数都继承了 Exception 这个父类,所以所有的...Error都是继承的 Exception 这个父类

注释事项:

1、因为py是按照顺序执行代码,所以不能将 except 放置最前

2、如果是填写的父进程,按照顺序,就会执行了父进程

 

所以 Exception 一定要放在子函数的最下面

except ValueError:
    print("上面的格式有问题")
except ZeroDivisionError:
    print("被除数不能是0")
except Exception:
    print("Exception 异常")

3、try 中在遇到报错之前的代码是已经加载到内存中的,不影响

try:
    a = 10
    n1 = int(input("请输入数字:"))
    print(n1)
except:
    print("您未输入数字")
    print(a)

2.5、try...except....else....

1、try 代码无异常,执行 try 代码,不执行 except 代码,执行else 代码,执行后续代码,可以理解 try  是 if ,try 对应的就有 else,而 else 写不写都行

2、try 代码异常,不执行 try 代码执行 except 代码执行 else 代码,执行后续代码

2.6、函数体 try 、except 存在 return

如果 try 中存 return ,则直接退出函数,后续代码无法执行,所以函数体 try 中不能存在 return。

def func():
    try:
        n1 = int(input("请输入数字:"))
        return 1
    except:
        print("您未输入数字")
        return 2
    else:
        print("这里是else")
    print("数字输入完毕")

a = func()
print(a)

 如果 except 中存 return ,会执行return ,但不会执行else,后续代码会继续执行

2.7、如何知道错误原因

出错了,如何打印出错原因

except ValueError as err:
    print("上面的格式有问题" , err)

2.7、try...except....finally....

try:
    # 可能出现的异常代码
except:
    # 如果有异常执行的代码
finally:
    # 无论有没有异常,都会执行的代码

使用场景:如文件操作、数据库操作,都需要clone

文件操作,stream = open(....)  streat.read()   stream.clone()  打开一个文件,假如打开的时候是mode = r ,但是read的时候是一个图片,会导致无法读取,所以在 try 中需要写 open() 和read()和stream.clone() ,而如果存在异常,则 except 中打印 文件存在异常,还要写stream.clone(),因为在try 中执行了open() ,是后续read()出错

def func():
    stream = None
    try:
        stream = open(r'E:\code\dir1\book\user1.txt')
        container = stream.read()
        print(container)
    except Exception as err:
        print(err)
    finally:
        print('-----finally------')
        if stream:
            stream.close()

func()

2.8、try...except...esle...finally.... 变量

1、try...except...finally.... 模块中代码相互隔离的,可以理解为是函数,局部函数与局部函数中的代码相互隔离

2、try...except...esle...finally....,在上述我们知道,try 代码无异常,执行 try 代码,不执行 except 代码,执行 else 代码,执行 finally 代码,执行后续代码,可以理解 try  是 if ,try 对应的就有 else,而 else 写不写都行,所以 try  else 是一个模块。所以try...esle... 下的代码是一块的

3、最后都会执行 finally ,所以 finally  下的代码并不是不能使用 try 或者 except 下代码中的变量,而且在代码执行之前,finally 并不知道上述执行的是 try 下的代码还是 except 下代码。所以说,finally  是可以引用已经执行的代码中的变量,但是在声明的时候,它并不知道是 try 下的代码还是 except 下代码,所以在声明的时候,可以在全局变量中声明,等代码运行后,在通过局部变量对重新赋值。

案例:1、为什么添加 stream = None 就不会报错,上述阐明,是因为定义的时候,不知道是运行的try 下的代码还是except 下的代码,如果是except 是没有stream的变量,所以先需要声明全局变量。2、需要知道,stream = open(r'E:\code\dir1\book\user1.txt'),如果不存在,open是打不开的,也就是说,如果不存在,在内存中根本无此变量。所以,假如try 执行stream = open(r'E:\code\dir1\book\user1.txt') 报错,在运行finally,if stream 是假,因为现在的 stream 等于全局变量的 None。

def func():
    stream = None
    try:
        stream = open(r'E:\code\dir1\book\user1.txt')
        container = stream.read()
    except Exception as err:
        print(err)
        stream = open(r'E:\code\dir1\book\user.txt')
        container1 = stream.read()
    else:
        a = [7, 8]
    finally:
        if stream:
            stream.close()


func()

 

注意:上述我们讲过,finally  下的代码并不是不能使用 try 或者 except 下代码中的变量,而且在代码执行之前,finally 并不知道上述执行的是 try 下的代码还是 except 下代码。所以说你可以忽略他的警告,但是如果确定在 try 中 stream 是存在的,一定会有stream变量,运行到finally后,执行stream.clone() 无任何问题,如果 stream 是不存在的,在 finally 中,不管是 if stream 还是 stream.clone() 都会报错,因为就没有 stream 这个变量,无法判断 和 clone 

 

2.9、return 返回数值

1、try else 模块,在 try 下添加 return ,else一定是不运行的

2、程序无问题,执行 try 下的程序遇到 return 1,此时可以理解为 return = 1,但此时并不会直接退出,而且继续运行 finally ,直到结束后遇到了 return 3,将 return = 3。程序报错,执行 except下的程序遇到 return 2,此时可以理解为 return = 2,但此时并不会直接退出,而且继续运行 finally ,直到结束后遇到了 return 3,将 return = 3。

def func():
    stream = None
    try:
        stream = open(r'E:\code\dir1\book\user.txt')
        container = stream.read()
        return 1
    except Exception as err:
        print(err)
        return 2
    finally:
        if stream:
            stream.close()
        return 3

a = func()
print(a)

 

 

2.10 、抛出异常 raise 

raise  为自定义抛出什么样子的异常

# 注册 用户名必须6位

def register():
    username = input("请输入用户名:")
    if len(username) < 6:
        raise Exception('用户名必须是6位以上')
    else:
        print("输入的用户名是:", username)

register()

raise  结合 try  except ,在之前已经讲述了 except Exception as err: 的作用

# 注册 用户名必须6位

def register():
    username = input("请输入用户名:")
    if len(username) < 6:
        raise Exception('用户名必须是6位以上')
    else:
        print("输入的用户名是:", username)

try:
    register()
except Exception as err:
    print(err)

三、生成器

什么是生成器:

1、生成器依赖于列表、集合、字典推导式
2、创建一个列表会将所有的列表的信息存入导内存中,但是由于内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面的几个元素,那后面的绝大数元素占用得到空间就浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必要创建完整的list,从而节省大大量空间。在python中,这种一边循环一边计算的机制,叫做生成器:generator。使用方法跟列表推导致一致,只不过使用()

3.1、生成一个生成器

newlist = [x*3 for x in range(20)]
print(newlist) # [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57]

g = (x*3 for x in range(20))
print(g)  # <generator object <genexpr> at 0x0000014B84DB65E0> 打印的是生成器的地址
print(type(g))  # <class 'generator'> 说明是生成器

调用生成器中的内容

方式一:调用 g.__next__() 执行一次打印一个

g = (x*3 for x in range(20))
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

 方式二:next(g) 执行一次打印一个,此方法是系统内置的

g = (x*3 for x in range(20))
print(next(g))
print(next(g))
print(next(g))

如果超出生成器给出的数值,将会提示 StopIteration 

g = (x*3 for x in range(5))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

使用 try except 解决 超出生成器给出的数值,将会提示 StopIteration 

g = (x*3 for x in range(5))
while True:
    try:
        e = next(g)
        print(e)
    except:
        break

3.2、函数生成器

在函数中带有 yield ,则此函数不是函数,而是变成生成器,实现函数的机制,只要需要生成器,直接调用用函数生成器。格式:函数中带有yield,用变量接收调用函数方法,并使用 next() 打印

def func():
    n = 0
    while True:
        n += 1
        # print(n)
        yield n

a = func()
print(a)
print(next(a))
print(next(a))

使用debug工具分析

在执行到 a = func() 后,点击进去代码中,发现是不进入生成器

 在执行到 print(next(a)) 后,点击进去代码中

 

会发现执进制了生成器,所以只有调用 next 后才会进入生成器

执行 yield 后,发现 n 变成了 1,所以这个 yield 类似 retutn + 暂停,暂停的意思是,停留在这个位置,等下次在调用此生成器的时候,会继续在此基础上在运行

3.3、函数生成器 return

生成器是没有任何数据可以产生了,想给出一个提示,往往添加到 return

def func(a):
    n = 0
    while n < a:
        n += 1
        yield n
    return "已完毕"

a = func(5)
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))

3.4、斐波那切数列+生成器

def lib(len):
    a, b = 0, 1
    n = 0

    while n < len:
        print(b)
        a, b = b, a + b
        n += 1

lib(8)

def lib(len):
    a, b = 0, 1
    n = 0

    while n < len:
        yield b
        a, b = b, a + b
        n += 1

g = lib(8)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

3.5、调用生成器 send()

提示,在你的的生成器开始的时候,不能发送一个非None的值,意思是,第一个数值给None

def gen():
    i = 0
    while i < 5:
        temp = yield i
        print('temp:', temp)
        i += 1
    return '没有更多的元素'


g = gen()
print('send:', g.send(None))
n1 = g.send("hehe")
print('n1:', n1)
n2 = g.send("haha")
print('n2:', n2)

send() 跟  g.__next__()  和 next(g) 的区别就是,每次可以传递一个参数,生成器中使用temp = yield i ,使用 temp 去接收传递的参数

解释一下运行顺序:

 
 

第一次执行 print('send:', g.send(None)) ,进入生成器,执行 temp = yield i,但不往下执行,所以print('temp:', temp) 不打印,所以只输出了send: 0

第二次执行 print("send:", g.send("hehe")),进入生成器,执行 temp = yield i,往下执行,所以print('temp:', temp) 打印成 temp: hehe,直到遇到return,暂停返回输出yield i 中的 i ,然后执行 print("send:", g.send("hehe")) 打印 end: 1

3.6、生成器作用--协程

进程中可以包含多个线程,一个线程中包含多个协程

一个调用函数,开放一个协程处理,但是如果是生成器,没执行一次开发暂停一次,在卡方一次协程,几个任务交替运作,而不是先执行一个完成后,在执行另一个

def task1(n):
    for i in range(n):
        print("在搬第{}块砖".format(i))
        yield

def task2(n):
    for i in range(n):
        print("在听第{}首歌".format(i))
        yield


g1 = task1(6)
g2 = task2(6)

while True:
    try:
        g1.__next__()
        g2.__next__()
    except:
        break

四、迭代器和可迭代对象

4.1、可迭代对象

什么是可迭代的?每调用一下,生成一个新的元素,叫做可迭代的
可迭代的都有那些?1、生成器  2、列表、集合、字典、字符串、元组

判断列表是不是可迭代的:

isinstance 是判断的意思
Iterable 是可迭代的

判断字符串是不是可迭代的:

判断整型是不是可迭代的:

 判断生成器是不是可迭代的:

4.2 、迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator 。

可迭代的 ≠ 迭代器对象,比如列表并不能被next() 调用

而生成器可以,所以生成器一定是迭代器

iter() 函数让列表、集合、字典、字符串、元组变成迭代器

# 列表
list1 = [1, 2, 3, 4, 8, 8]
list1 = iter(list1)
print(next(list1))
print(next(list1))

# 集合
set1 = {1, 2, 3, 4, 8, 8}
set1 = iter(set1)
print(next(set1))
print(next(set1))

# 字典
dict1 = {'key': 'value', 'key2': 'value2'}
dict1 = iter(dict1)
print(next(dict1))
print(next(dict1))

# 字符串
str = "123asd"
str = iter(str)
print(next(str))
print(next(str))

# 元组
tuple1 = (1, 2, 3, 4, 8, 8)
tuple1 = iter(tuple1)
print(next(tuple1))
print(next(tuple1))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值