python中生成器和迭代器区别、异常捕获、多进程、多线程

迭代:迭代是一种操作,比如使用for循环,逐个获取数据的这个过程就叫迭代

d1 = [1,2,3]
for i in d1:
    print(i)

可迭代对象:对象的的特性,比如[]、{}、()、字符串,文件对象都是可以迭代的,可以做for循环

d2 = [1,2,3]
d3 = {'a':1,'b':2}
d4 = (1,2,3)
for i in d2:
    print(i)
for i in d3:
    print(i)
for i in d4:
    print(i)

迭代器:支持next()操作的对象,它包含了一组元素,当执行next()操作时,返回其中一个元素,当所有元素被返回时,在执行next()就是爆出异常StopIteration

d5 = [1,2,3]
a = iter(d5)
# len(a)            # 迭代器不能使用len函数
b1 = next(a)
print(b1)
b2 = next(a)
print(b2)
b3 = next(a)
print(b3)
b4 = next(a)
print(b4)           # 抛出异常:StopIteration

迭代器类中的运用:类中有__iter__方法和__next__方法的就叫迭代器类

class Duoceshi:                  
    def __init__(self):
        self.counter = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == 4:
            raise StopIteration()
        return self.counter

根据类实例化创建一个 迭代器对象

d = Duoceshi()            # 通过这个迭代器类创建的这个对象d,就叫迭代器对象

普通调用方式:

v1 = d.__next__()
print(v1)
v2 = d.__next__()
print(v2)
v3 = d.__next__()
print(v3)
v4 = d.__next__()
print(v4)               # 抛出异常:StopIteration

next()方法调用

v1 = next(d)
print(v1)
v2 = next(d)
print(v2)
v3 = next(d)
print(v3)
v4 = next(d)
print(v4)             # 抛出异常:StopIteration

实际工作中的写法时使用for循环

p = Duoceshi()
for item in p:     # 首先回去执行迭代器对象的__iter__方法,获取返回值,一直反复的去执行next(对象)
    print(item)    # 打印结果为:1 2 3

迭代器总结:

1、迭代器对象支持通过 next()取值,如果取值结束,则自动抛出异常StopIteration
2、for 循环内部在循环的时候,就是先执行的__iter__方法,获取一个迭代器对象,然后不断的执行
3、next方法取值(有异常则StopIteration终止异常)

生成器:带有yield的函数,他记住了上一次返回时的函数具体位置
生成器不仅“记住”了它的数据状态,生成器还记住了程序执行的位置
生成器一定是可以迭代的,也一定是迭代器对象

iter1 = [i for i in range(3)]
iter2 = (i for i in range(3))     # 就变成了生成器
print(iter1)
print(iter2)

a1 = next(iter2)
print(a1)
a2 = next(iter2)
print(a2)
a3 = next(iter2)
print(a3)
a4 = next(iter2)
print(a4)                          # 抛出异常:StopIteration

创建生成器函数,只要在函数中有yield方法,就是 一个生成器函数

def test():
    mylist = range(3)
    for i in mylist:
        yield i

data = test()
print(data)

a1 = next(data)
print(a1)
a2 = next(data)
print(a2)
a3 = next(data)
print(a3)
a4= next(data)
print(a4)             # 抛出异常:StopIteration

创建一个生成器对象,(内部会根据生成器类generator创建对象),

生成器类的内部也声明了:__iter__、__next__方法

所以按照迭代器的定义规则来看,其实生成器又可以叫做一个特殊的迭代器

def func():
    yield 1
    yield 2
    yield 3
f = func()         # 通过生成器类创建的对象,就叫生成器对象
v1 = next(f)
print(v1)
v2 = next(f)
print(v2)
v3 = next(f)
print(v3)
v4= next(f)
print(v4)          # 报错,抛出异常:StopIteration
f1 = func()
for item in f1:
    print(item)    # # 打印结果为:1 2 3

如果一个类中有__iter__方法且返回一个迭代器对象,则我们称为这个类创建的对象为可迭代对象

class Fand(object):
    def __iter__(self):
        return # 迭代器对象(生成器对象)

f = Fand() # 这里创建的f就是一个可以迭代的对象

可迭代对象是可以使用for循环,在循序的内部其实先执行了__iter__方法,
获取迭代器对象,然后在内部执行迭代器对象的next方法,逐步获取值

实例:

class Duoceshi2:    # 创建的类中有__iter__方法和__next__方法的就叫迭代器类
    def __init__(self):
        self.counter = 0
    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == 4:
            raise StopIteration() # raise:(瑞日)
        return self.counter

class Fand:
    def __iter__(self):
        return Duoceshi2() # 返回迭代器对象

f = Fand()         # 创建的f就是一个可迭代对象,迭代对象不能使用len函数

for item in f:     # 循环可迭代对象时,内部会先执行f.__iter__并获取迭代器对象,不断的执行迭代器对象中的next方法
    print(item)    # 打印结果为:1 ,2 ,3

迭代器和生成器区别:
生成器是生成元素的,迭代器是访问集合元素的一种方式
迭代器是一种支持next()操作对象的,迭代器输出生成器的内容
迭代器:其中iterator对象表示的是一个数据流,可以看作是一个有序序列,
不能提前知道序列的长度,只能通过next()获得下一个数据

异常捕获

# 1. 自定义异常类, 继承Exception, 魔法方法有init和str(设置异常描述信息)
class ShortInputError(Exception):
    def __init__(self, length, min_len):
        # 用户输入的密码长度
        self.length = length
        # 系统要求的最少长度
        self.min_len = min_len

    # 设置异常描述信息
    def __str__(self):
        return f'您输入的密码长度是{self.length}, 密码不能少于{self.min_len}'

def main():
    # 2. 抛出异常: 尝试执行:用户输入密码,如果长度小于3,抛出异常
    try:
        password = input('请输入密码:')
        if len(password) < 3:
            # 抛出异常类创建的对象
            raise ShortInputError(len(password), 3)
    # 3. 捕获该异常
    except Exception as result:
        print(result)
    else:
        print('没有异常,密码输入完成')

    finally:
        print("结束")         # 不管有没有异常,finally 都会执行

main()



 

多进程的使用

# 1. 导入进程包
import multiprocessing
import time

# 跳舞任务
def dance():
    for i in range(3):
        print("跳舞中...")
        time.sleep(0.2)

# 唱歌任务
def sing():
    for i in range(3):
        print("唱歌中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 2. 创建子进程(自己手动创建的进程称为子进程, 在__init__.py文件中已经导入的Process类)
    # 1. group: 进程组,目前只能使用None,一般不需要设置
    # 2. target: 进程执行的目标任务
    # 3. name: 进程名,如果不设置,默认是Process-1, ......
    dance_process = multiprocessing.Process(target=dance)
    sing_process = multiprocessing.Process(target=sing)

    # 3. 启动进程执行对应的任务
    dance_process.start()
    sing_process.start()
    # 进程执行是无序的,具体那个进程先执行是由操作系统调度决定

获取进程编号

# 1. 导入进程包
import multiprocessing
import time
import os

# 跳舞任务
def dance():
    # 获取当前进程(子进程)的编号
    dance_process_id = os.getpid()
    # 获取当前进程对象,查看当前代码是由那个进程执行的 : multiprocessing.current_process()
    print("dance_process_id:", dance_process_id, multiprocessing.current_process())
    # 获取当前进程的父进程编号
    dance_process_parent_id = os.getppid()
    print("dance_process的父进程编号是:", dance_process_parent_id)

    for i in range(3):
        print("跳舞中...")
        time.sleep(0.2)
        # 扩展: 根据进程编号强制杀死指定进程
        os.kill(dance_process_id, 9)

# 唱歌任务
def sing():
    # 获取当前进程(子进程)的编号
    sing_process_id = os.getpid()
    # 获取当前进程对象,查看当前代码是由那个进程执行的 : multiprocessing.current_process()
    print("sing_process_id:", sing_process_id, multiprocessing.current_process())

    # 获取当前进程的父进程编号
    sing_process_parent_id = os.getppid()
    print("sing_process的父进程编号是:", sing_process_parent_id)

    for i in range(3):
        print("唱歌中...")
        time.sleep(0.2)

# 获取当前进程(主进程)的编号
main_process_id = os.getpid()
# 获取当前进程对象,查看当前代码是由那个进程执行的 : multiprocessing.current_process()
print("main_process_id:", main_process_id, multiprocessing.current_process())

if __name__ == '__main__':
    # 2. 创建子进程(自己手动创建的进程称为子进程, 在__init__.py文件中已经导入的Process类)
    # 1. group: 进程组,目前只能使用None,一般不需要设置
    # 2. target: 进程执行的目标任务
    # 3. name: 进程名,如果不设置,默认是Process-1, ......
    dance_process = multiprocessing.Process(target=dance, name="dance_process")
    print("dance_process:", dance_process)
    sing_process = multiprocessing.Process(target=sing, name="sing_process")
    print("sing_process:", sing_process)

    # 3. 启动进程执行对应的任务
    dance_process.start()
    sing_process.start()

    # 进程执行是无序的,具体那个进程先执行是由操作系统调度决定

进程执行带有参数的任务

import multiprocessing

# 显示信息的任务
def show_info(name, age):
    print(name, age)

if __name__ == '__main__':
    # 创建子进程
    # 以元组方式传参,元组里面的元素顺序要和函数的参数顺序保持一致
    sub_process = multiprocessing.Process(target=show_info, args=("李四", 20))
    # 启动进程
    sub_process.start()

    # 以字典方式传参,字典里面的key要和函数里面的参数名保持一致,没有顺序要求
    sub_process = multiprocessing.Process(target=show_info, kwargs={"age":20, "name": '王五'})
    # 启动进程
    sub_process.start()
    sub_process = multiprocessing.Process(target=show_info, args=("冯七",), kwargs={"age": 20})
    # 启动进程
    sub_process.start()

进程之间执行是无序的

import multiprocessing
import time

def task():
    time.sleep(1)
    # 获取当前线程
    print(multiprocessing.current_process())

if __name__ == '__main__':
    # 循环创建大量进程,测试进程之间执行是否无序
    for i in range(20):
        # 每循环一次创建一个子进程
        sub_process = multiprocessing.Process(target=task)
        # 启动子进程执行对应的任务
        sub_process.start()

    # 结论: 进程之间执行也是无序的,是由操作系统调度进程来决定的

进程之间不共享全局变量

import multiprocessing
import time

print("xxx")
# 定义全局变量列表
g_list = list()  #=> []

# 添加数据的任务
def add_data():
    for i in range(3):
        # 因为列表是可变类型,可以在原有内存的基础上修改数据,并且修改后内存地址不变
        # 所以不需要加上global关键字
        # 加上global 表示声明要修改全局变量的内存地址
        g_list.append(i)
        print("add:", i)
        time.sleep(0.2)
    print("添加完成:", g_list)

# 读取数据的任务
def read_data():
    print("read:", g_list)

# 提示: 对应linux和mac主进程执行的代码不会进程拷贝,但是对应window系统来说主进程执行的代码也会进行拷贝执行
# 对应window来说创建子进程的代码如果进程拷贝执行相当于递归无限制进行创建子进程,会报错
# 如果解决windows递归创建子进程,通过判断是否是主模块来解决
# 理解说明: 直接执行的模块就是主模块,那么直接执行的模块里面就应该添加判断是否是主模块的代码

# 1. 防止别人导入文件的时候执行main里面的代码
# 2. 防止windows系统递归创建子进程
if __name__ == '__main__':
    # 添加数据的子进程
    add_process = multiprocessing.Process(target=add_data)
    # 读取数据的子进程
    read_process = multiprocessing.Process(target=read_data)

    # 启动进程执行对应的任务
    add_process.start()

    # 当前进程(主进程)等待添加数据的进程执行完成以后代码再继续往下执行
    add_process.join()
    print("main:", g_list)
    read_process.start()

# 结论: 进程之间不共享全局变量

# 扩展:
# fork 函数, 创建子进程都需要对主进程的代码进行拷贝
# fork mac 和 linux python解释器里面都有fork函数,通过fork函数去创建的子进程

主进程会等待所有的子进程执行结束再结束

import multiprocessing
import time

def task():
    # for i in range(10):
    while True:
        print("任务执行中...")
        time.sleep(0.2)

# 判断是否是直接执行的模块, 程序入口模块

# 标准python写法,直接执行的模块,需要加上判断是否是主模块的代码
if __name__ == '__main__':

    # 创建子进程
    sub_process = multiprocessing.Process(target=task)
    # 把子进程设置成为守护主进程,以后主进程退出子进程直接销毁
    # sub_process.daemon = True
    sub_process.start()

    # 主进程延时0.5秒钟
    time.sleep(0.5)
    # 退出主进程之前,先让子进程进行销毁
    sub_process.terminate()
    print("over")

# 结论: 主进程会等待子进程执行完成以后程序再退出

# 解决办法: 主进程退出子进程销毁
# 1. 让子进程设置成为守护主进程,主进程退出子进程销毁,子进程会依赖主进程
# 2. 让主进程退出之前先让子进程销毁

多线程的使用

# 1. 导入线程模块
import threading
import time

def sing():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("sing:", current_thread)

    for i in range(3):
        print("唱歌中...")
        time.sleep(0.2)

def dance():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("dance:", current_thread)

    for i in range(3):
        print("跳舞中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 获取当前线程
    current_thread = threading.current_thread()
    print("main_thread:", current_thread)

    # 2. 创建子线程
    sing_thread = threading.Thread(target=sing, name="sing_thread")
    dance_thread = threading.Thread(target=dance, name="dance_thread")
    # 3. 启动子线程执行对应的任务
    sing_thread.start()
    dance_thread.start()

线程执行带有参数的任务

import threading

def show_info(name, age):
    print("name: %s age: %d" % (name, age))

if __name__ == '__main__':
    # 创建子线程
    # 以元组方式传参,要保证元组里面元素的顺序和函数的参数顺序一致
    sub_thread = threading.Thread(target=show_info, args=("李四", 20))
    # # 启动线程执行对应的任务
    sub_thread.start()

    # 以字典的方式传参,要保证字典里面的key和函数的参数名保持一致
    sub_thread = threading.Thread(target=show_info, kwargs={"name": "王五", "age": 30})
    # 启动线程执行对应的任务
    sub_thread.start()

 线程之间执行时无序的

import threading
import time

def task():
    time.sleep(1)
    # 获取当前线程
    print(threading.current_thread())

if __name__ == '__main__':
    # 循环创建大量线程,测试线程之间执行是否无序
    for i in range(20):
        # 每循环一次创建一个子线程
        sub_thread = threading.Thread(target=task)
        # 启动子线程执行对应的任务
        sub_thread.start()

    # 线程之间执行是无序的,具体那个线程执行是由cpu调度决定的

主线程会等待子线程执行结束再结束

import threading
import time

def task():
    while True:
        print("任务执行中...")
        time.sleep(0.3)

if __name__ == '__main__':
    # 创建子线程
    # daemon=True 表示创建的子线程守护主线程,主线程退出子线程直接销毁
    # sub_thread = threading.Thread(target=task, daemon=True)
    sub_thread = threading.Thread(target=task)
    # 把子线程设置成为守护主线程
    sub_thread.setDaemon(True)
    sub_thread.start()

    # 主线程延时执行1秒
    time.sleep(1)

    print("over")
    # exit()

# 结论: 主线程会等待子线程执行结束再结束

# 解决办法: 把子线程设置成为守护主线程即可

线程之间共享全局变量

import threading
import time

# 定义全局变量
g_list = []

# 添加数据的任务
def add_data():
    for i in range(3):
        # 每循环一次把数据添加到全局变量
        g_list.append(i)
        print("add:", i)
        time.sleep(0.3)

    # 代码执行到此,说明添加数据完成
    print("添加数据完成:", g_list)

# 读取数据的任务
def read_data():
    print("read:", g_list)

if __name__ == '__main__':
    # 创建添加数据的子线程
    add_thread = threading.Thread(target=add_data)
    # 创建读取数据的子线程
    read_thread = threading.Thread(target=read_data)

    # 启动线程执行对应的任务
    add_thread.start()
    # time.sleep(1)
    # 让当前线程(主线程)等待添加数据的子线程执行完成以后代码在继续执行
    add_thread.join()
    read_thread.start()

# 结论: 线程之间共享全局变量

线程之间共享全局变量数据错误的问题

import threading

# 全局变量
g_num = 0

# 循环100万次执行的任务
def task1():
    for i in range(1000000):
        # 每循环一次给全局变量加1
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num = g_num + 1  # g_num += 1

    # 代码执行到此,说明数据计算完成
    print("task1:", g_num)

# 循环100万次执行的任务
def task2():
    for i in range(1000000):
        # 每循环一次给全局变量加1
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num = g_num + 1  # g_num += 1

    # 代码执行到此,说明数据计算完成
    print("task2:", g_num)

if __name__ == '__main__':
    # 创建两个子线程
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    # 启动线程执行任务
    first_thread.start()
    # 线程等待,让第一个线程先执行,然后在让第二个线程再执行,保证数据不会有问题
    first_thread.join() # 主线程等待第一个子线程执行完成以后代码再继续往下执行
    second_thread.start()

互斥锁

import threading

# 全局变量
g_num = 0

# 创建互斥锁, Lock本质上是一个函数,通过调用函数可以创建一个互斥锁
lock = threading.Lock()

# 循环100万次执行的任务
def task1():
    # 上锁
    lock.acquire()
    for i in range(1000000):
        # 每循环一次给全局变量加1
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num = g_num + 1  # g_num += 1

    # 代码执行到此,说明数据计算完成
    print("task1:", g_num)
    # 释放锁
    lock.release()

# 循环100万次执行的任务
def task2():
    # 上锁
    lock.acquire()
    for i in range(1000000):
        # 每循环一次给全局变量加1
        global g_num  # 表示要声明修改全局变量的内存地址
        g_num = g_num + 1  # g_num += 1

    # 代码执行到此,说明数据计算完成
    print("task2:", g_num)
    # 释放锁
    lock.release()

if __name__ == '__main__':
    # 创建两个子线程
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    # 启动线程执行任务
    first_thread.start()

    second_thread.start()

    # 互斥锁可以保证同一时刻只有一个线程去执行代码,能够保证全局变量的数据没有问题
    # 线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降

死锁: 一直等待对方释放锁的情景叫做死锁

# 死锁: 一直等待对方释放锁的情景叫做死锁
import threading

# 创建互斥锁
lock = threading.Lock()

# 需求: 多线程同时根据下标在列表中取值,要保证同一时刻只能有一个线程去取值
def get_value(index):
    # 上锁
    lock.acquire()
    my_list = [1, 4, 6]
    # 判断下标是否越界
    if index >= len(my_list):
        print("下标越界:", index)
        # 取值不成功,也需要释放互斥锁,不要影响后面的线程去取值
        # 锁需要在合适的地方进行释放,防止死锁
        lock.release()
        return

    # 根据下标取值
    value = my_list[index]
    print(value)
    # 释放锁
    lock.release()

if __name__ == '__main__':
    # 创建大量线程,同时执行根据下标取值的任务
    for i in range(10):
        # 每循环一次创建一个子线程
        sub_thread = threading.Thread(target=get_value, args=(i,))
        # 启动线程执行任务
        sub_thread.start()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值