menu ChaYedan
Python基础知识(四)
447 浏览 | 2020-02-17 | 阅读时间: 约 6 分钟 | 分类: Python | 标签: Python
请注意,本文编写于 955 天前,最后修改于 831 天前,其中某些信息可能已经过时。

概念

  • 包 是一个 包含多个模块 的 特殊目录
  • 目录下有一个 特殊的文件 __init__.py
  • 包名的 命名方式 和变量名一致,小写字母 + _

__init__.py的作用:

要在外界使用 中的模块,需要在__init__.py中指定对外界提供的模块列表

# 从 当前目录 导入 模块列表
from . import send_message
from . import receive_message

作用

使用import能一次性导入包中所有的模块

多任务

写在最前,Python中多任务有三个,进程,线程,协程。

协程在这里没写,要看协程看移动硬盘里的Python2018

概念

多任务是指在同一时间内执行多个任务,例如: 现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。

执行方式

  • 并发:在一段时间内交替去执行任务。
  • 并行:对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行

进程

概念

一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。

注意:

一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程执行任务的是线程

使用方式
  1. 导入包

    #导入进程包
    import multiprocessing
  2. 填入参数(Process是一个类)

Process([group [, target [, name [, args [, kwargs]]]]])

  • group:指定进程组,目前只能使用None
  • target:执行的目标任务名
  • name:进程名字
  • args:以元组方式给执行任务传参
  • kwargs:以字典方式给执行任务传参

Process创建的实例对象的常用方法:

  • start():启动子进程实例(创建子进程)
  • join():等待子进程执行结束
  • terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:

name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

  1. start启动执行任务

    import multiprocessing
    import time
    
    
    # 跳舞任务
    def dance():
        for i in range(5):
            print("跳舞中...")
            time.sleep(0.2)
    
    
    # 唱歌任务
    def sing():
        for i in range(5):
            print("唱歌中...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        # 创建跳舞的子进程
        # group: 表示进程组,目前只能使用None
        # target: 表示执行的目标任务名(函数名、方法名)
        # name: 进程名称, 默认是Process-1, .....
        dance_process = multiprocessing.Process(target=dance, name="myprocess1")
        sing_process = multiprocessing.Process(target=sing)
    
        # 启动子进程执行对应的任务
        dance_process.start()
        sing_process.start()

获取进程编号

获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。

获取进程编号的两种操作

  • 获取当前进程编号os.getpid()
  • 获取当前父进程编号os.getppid()

关于传参数

用args(元组)传参时,只传一个参数时,注意要加一个逗号,否则会认为是一个普通的参数。使用的是顺序传参

例如:(3,)

用kwargs(字典)传参时,注意用大括号。key一定要与参数列表中的一样。使用的是关键字传参

注意点
  • 进程之间不共线全局变量
  • 主进程会等待所有的子进程执行结束后再结束

线程

概念

线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度 ,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。

使用方式
  1. 导入threading模块

    #导入线程模块
    import threading
  2. 创建子线程对象

Thread([group [, target [, name [, args [, kwargs]]]]])

    • group: 线程组,目前只能使用None
    • target: 执行的目标任务名
    • args: 以元组的方式给执行任务传参
    • kwargs: 以字典方式给执行任务传参
    • name: 线程名,一般不用设置
    • daemon:是否守护主线程
    1. 启动线程执行相应的任务

    启动线程使用start方法

    import threading
    import time
    
    # 唱歌任务
    def sing():
        # 扩展: 获取当前线程
        # print("sing当前执行的线程为:", threading.current_thread())
        for i in range(3):
            print("正在唱歌...%d" % i)
            time.sleep(1)
    
    # 跳舞任务
    def dance():
        # 扩展: 获取当前线程
        # print("dance当前执行的线程为:", threading.current_thread())
        for i in range(3):
            print("正在跳舞...%d" % i)
            time.sleep(1)
    
    
    if __name__ == '__main__':
        # 扩展: 获取当前线程
        # print("当前执行的线程为:", threading.current_thread())
        # 创建唱歌的线程
        # target: 线程执行的函数名
        sing_thread = threading.Thread(target=sing)
    
        # 创建跳舞的线程
        dance_thread = threading.Thread(target=dance)
    
        # 开启线程
        sing_thread.start()
        dance_thread.start()

    传参数跟进程一样

    注意点
    • 线程之间执行是无序的
    • 主线程会等待所有的子线程执行结束再结束
    • 线程之间共享全局变量
    • 线程之间共享全局变量数据出现错误问题

    互斥锁

    概念

    互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。

    注意:

    • 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。
    使用方式

    threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。

    互斥锁使用步骤:

    # 创建锁
    mutex = threading.Lock()
    
    # 上锁
    mutex.acquire()
    
    ...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定...
    
    # 释放锁
    mutex.release()

    注意点:

    • acquire和release方法之间的代码同一时刻只能有一个线程去操作
    • 如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。

    死锁

    概念

    死锁: 一直等待对方释放锁的情景就是死锁

    死锁的结果

    • 会造成应用程序的停止响应,不能再处理其它任务了。

    闭包

    定义

    在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

    构成

    通过闭包的定义,我们可以得知闭包的形成条件:

    1. 在函数嵌套(函数里面再定义函数)的前提下
    2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
    3. 外部函数返回了内部函数

    DEMO

    # 定义一个外部函数
    def func_out(num1):
        # 定义一个内部函数
        def func_inner(num2):
            # 内部函数使用了外部函数的变量(num1)
            result = num1 + num2
            print("结果是:", result)
        # 外部函数返回了内部函数,这里返回的内部函数就是闭包
        return func_inner
    
    # 创建闭包实例    
    f = func_out(1)
    # 执行闭包
    f(2)
    f(3)

    运行结果:

    结果是: 3
    结果是: 4

    闭包执行结果的说明:

    通过上面的输出结果可以看出闭包保存了外部函数内的变量num1,每次执行闭包都是在num1 = 1 基础上进行计算。

    作用

    • 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。

    注意点:

    • 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

    修改外部变量的值

    使用nonlocal关键字

    # 定义一个外部函数
    def func_out(num1):
    
        # 定义一个内部函数
        def func_inner(num2):
            # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
            nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
            # 修改外部变量num1
            num1 = 10
            # 内部函数使用了外部函数的变量(num1)
            result = num1 + num2
            print("结果是:", result)
    
        print(num1)
        func_inner(1)
        print(num1)
    
        # 外部函数返回了内部函数,这里返回的内部函数就是闭包
        return func_inner
    
    # 创建闭包实例
    f = func_out(1)
    # 执行闭包
    f(2)

    小结

    1. 当返回的内部函数使用了外部函数的变量就形成了闭包
    2. 闭包可以对外部函数的变量进行保存
    3. 实现闭包的标准格式:

      # 外部函数
      def test1(a):
       b = 10
       # 内部函数
       def test2():
           # 内部函数使用了外部函数的变量或者参数
           print(a, b)
       # 返回内部函数, 这里返回的内部函数就是闭包实例
       return test2

    每个闭包都是独立的

    知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

    发表评论

    email
    web

    全部评论 (暂无评论)

    info 还没有任何评论,快来留言吧!