python系统编程

系统编程

系统工具

概述

python系统模块:

模块名作用
*sys负责导出与怕以后呢解释器本身相关的组件
*os包含与python所在底层操作系统相应的变量和函数
*os.path为文件和目录处理工具提供了可移植的接口
glob用于文件名扩展
socket用于网络连接和进程间通信
threading,_thread,queue用于运行和同步化并发线程
subprocess,multiprocessing用于启动和控制并行进程
signal,select,shutil,tempfile用于多种系统相关任务
第三方扩展以下
pySerial串行端口接口
Pexpect用于控制程序间对话
Twisted网络框架

获取模块文档:

  • 获取属性:dir()

  • 获取用法:__doc__或help()

sys模块

工具作用
*sys.platform底层操作系统名称
sys.maxsize当前计算机可容纳的最大整型
sys.versionpython解释器版本号
*sys.path正在运行的python解释器真正的模块搜索路径
包含代表脚本主目录的指示器(首项),交互模式下是空字符串
可以更改,并不是永久性的
*sys.modules字典,python进程所导入的每一个模块
sys.getrefcount查看对象的引用次数
sys.builtin_module_namespython可执行程序的内置模块名称
sys.exc_info()元祖,最近异常的类型,值,追踪对象
追踪对象可用模块traceback处理
将追踪对象传入traceback.print_tb()
sys.argv命令行参数
stdin,stdout,stdrrr标准流
sys.exit强制退出

os模块

提供了POSIX(可移植操作系统接口)工具,不依赖平台的目录处理,os.path

常用的os工具:

任务工具
shell变量os.environ:设置和获取shell环境变量
运行程序os.execv
os.execlp:启动新程序
os.spawnv:启动带有底层控制的新程序
os.system():在pythin脚本中运行shell命令,会暂停它的调用者,可在命令后加&
os.popen():运行shell命令并与其输入流(传入’w’参数,write()方法)或输出流(默认,read()方法)相连接.
os.startfile():用相应的软件打开文件,无论文件是什么
派生程序os.waitpid,os.kill
os.fork:在类Unix系统下派生新的子进程
os.pipe:负责进程间通信
文件描述符,文件锁os.resd,os.write
os.open:打开基于底层描述符的文件
os.stat:获取文件底层信息
文件处理os.rename,os.rmdir
os.walk:将函数和循环运用与整个目录树的各部分
os.remove:根据路径名删除文件
os.mkdir:创建新目录
os.mkfifo:创建新的命名管道
管理工具:提供信息,帮助管理os.chmod,os.listdir,os.access
os.getpid():给出掉用函数的进程的id
os.getcwd():返回当前目录
os.chdir():改变目录
移植工具os.path.split(‘path’):将路径分割为目录和文件
os.path.splitext(‘path’):分割了文件的扩展名
os.path.normpath(‘path’:将路径分隔符统一为平台的目录分隔符)
os.path.abspath(‘path’):返回文件的完整目录路径名.如添加前缀和处理…语法
os.path.join:将目录和文件合成路径
os.path.dirname(‘path’)/basename(‘path’):返回目录/文件
os.sep:目录分割符号
os.pathsep:目录列表中分隔目录的符号
os.pardir:父目录
os.curdir:当前目录
os.linesep:换行符
路径名工具os.path.getsize(‘path’):通过文件名获取文件大小
os.path.isdir(‘path’)/isfile(‘path’):检测文件类型,是目录/文件
os.path.exists(‘path’):测试文件是否存在

脚本运行上下文

当前工作路径

当前工作路径(CWD)与脚本所在路径区别:

  • 当前工作路径(CWD):
    • 当前工作路径是启动脚本的路径,即输入命令行的地方.
    • 脚本中没有路径的文件名将会映射到此.
    • 通过os.getcwd()获得,os.chdir()改变.
    • 当通过图标执行一个脚本时,cwd会被设置为脚本所在路径.
  • 脚本所在路径:
    • 脚本所在路径是脚本文件物理存储位置.
    • import导入时,最先搜索的目录.
    • 可以通过sys.path列表中的首项看到.

命令行参数

sys.argv:得到命令行参数列表.sys.argv[0]为执行脚本的名称,在命令行出现的顺序决定列表中的索引

python中的命令行处理工具解析更复杂的命令行:

  • getopt模块
  • optparse模块,功能更强大

note:
unix上的可执行脚本:
脚本第一行#!/usr/bin/env指定解释器
chmod u+x scripname:增加可执行权限

shell环境变量

python通过一个类似python字典的对象os.environ访问环境变量

获取环境变量:

  • os.environ[‘环境变量名’],例如获取PYTHONPATH:os.environ[‘PYTHONPATH’]

修改环境变量:

  • 通过对os.environ[‘环境变量名’]赋值,在内部调用os.putenv改变环境变量(修改是临时的,只对程序本身和子程序有效)

    • 子程序:由Unix下的os.spawnv,os.fork/exec,以及所有平台的os.popen,os.system,subprocess启动的程序

    • 修改是临时的原因:一个子程序始终从父程序继承环境变量,而子程序的环境变量不会传递给父进程.

标准流

标准流是预先打开的文件对象,python启动时被绑定到控制台窗口,sys模块提供了标准输入(sys.stdin),标准输出(sys.stdout)和错误流(sys.stderr)

重定向流到文件或程序(依赖shell命令行):

  • 标准输入流重定向到文件输入:< fielname

  • 标准输出流重定向到文件:>filename

  • 结合使用:< inputfile > outputfile

  • 管道,一个程序的标准输出发送到另一个程序的标准输入,python脚本可以在任意一端:|

重定向流与用户交互:

  • 当输入流被重定向后,linux中可以通过/dev/tty文件(当前虚拟终端)读取键盘输入

  • 文件的isatty()方法探测文件是否连接到控制台

重定向流到python对象:

  • 任何提供了类似文件read方法的对象可以指定给sys.stdin,以从该对象的read方法读取输入

  • 任何定义了类似文件write方法的对象可以指定给sys.stdout,所有标准输出将发送到该对象方法上

  • 需要保存和重置原来的流

io标准库:

```python
from io import StringIO, BytesIO

# StringIO提供对象,将文件对象接口和内存字符串相映射(文本)

buff = StringIO()  # 在字符串中保存写入的文本
buff.write('string\n')
buff.getvalue()  # 取出值

buff = StringIO('string\nstream\n')  # 从字符串中读取输入值
buff.readline() 

# ByteIO提供对象,将文件操作映射到内存字节缓冲区(二进制)

stream = BytesIO()  # 在字节缓冲区中保存写入的文本
stream.write(b'stream')
stream.getvalue()

stream = BytesIO(b'stream')  # # 从字节缓冲区中读取输入值
stream.read()
```

print调用中的重定向语法:

  • print(string, file=filename) #filename为文件名,需要打开的,有写入权限的

subprocess模块:

  • 运行命令的替代方案,对流的控制更完善,控制的是子程序的流.

  • subprocess.call(‘cmd’,shell=True),shell与平台相关,类Unix上为真时,由shell执行程序,否则由os.execvp运行

  • 将stdout流与管道连接,然后用communicate来运行命令,并接收它的标准输出流和错误流文件

    from subprocess import Popen, PIPE
    
    pipe = Popen('python hello.py', stdout=PIPE, stdin=PIPE) # 获取派生程序的输出流和输入流
    pipe2 = Popen('python reader.py', stdin=pipe.stdout) # 将pipe的输出流给pipe2的输入流
    pipe.stdin.write('string') #写入输入流
    pipe.stdin.close()  # 关闭输入流文件
    pipe.stdout.read() # 读取输出流
    
    # pipe.communicate() # 
    # pipe.returncode  # 查看退出状态,成功一般是0
    pipe.wait()  # 退出状态
    

os.popen:

```python
import os

pipe = os.popen('python hello_out.py')  # 默认对stdout读
pipe.read()
print(pipe.close())  # None代表没有错误

pipe = os.popen('python hello_in.py', 'w')  # 对stdin写
pipe.write('Gumby') 
pipe.close()
```

文件和目录工具

文件工具

内建文件对象

  • 支持多种方法:将缓冲区写入磁盘(‘flush’),释放系统资源( ‘close’),获取底层文件句柄(‘fileno’),读取数据(‘read’, ‘readline’, ‘readlines’),移动到文件的任意位置(‘seek’),返回当前位置(‘tell’),写入数据(‘writable’, ‘write’, ‘writelines’)

  • open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

    • file:文件名,也可以是文件描述符

    • mode:r可读(默认);w可写,并擦除以前任何内容;a追加;+可读可写;b以二进制方式打开

    • buffering:0无缓存,二进制模式;1逐行缓冲;其他全缓冲(默认)

    • encoding:编码设置

  • seek(N, whence=0)

    • 0:绝对的开始位置,偏移量非负

    • 1:相对前位置,偏移量可正可负

    • 2:绝对的末尾位置,偏移量一般为负

用struct模块解析打包的二进制数据:

import struct  # 导入包
data = struct.pack('>i4shf', 2, 'spam', 3, 1.234)  # 按照特定格式打包,>表示高位优先,i是四字节整数,4s代表四字节字符串,h是二字节整数,f是浮点数
value = struct.unpack('>i4shf', data)  # 按照同样的格式解包 

os模块中的底层文件工具

  • os.open(path, flags, mode):打开文件并返回其描述符.描述符是整数代码或句柄,用来标识文件,mode是模式标识符,提供更多的底层控制.

    • O_EXCL:唯一访问权

    • O_NONBLOCK:非阻塞模式

    • (os.O_RDWR|os.O_BINARY):相当与rb+

  • os.read(descriptor, N):最多读取N个字节并返回一个字节字符串,descriptor是描述符.

  • os.write(descriptor, string):把字节字符串string中的字节写入文件.

  • os.lseek(descriptor, position, how):在文件中移至position,how相当与seek中的模式.

  • os.fdopen(descriptor, ‘rb’):将文件描述符封装成文件对象,第二个参数是文件对象模式

  • 其他os模块文件工具

  • os.chmod(filepath, Ooxxx):更改文件权限.文件路径和三位八进制(与Unix下文件设置相同)

  • os.renaem(oldname, newname):更改名称

  • os.remove(filename):删除文件

  • os.stat:(fielname):文件的底层信息

目录工具

遍历目录

  • os.popen():运行shell列表命令,例如unix中ls,windows中dir.与命令耦合,不跨平台

  • glob.glob()模块:接受路径文件名(每一级路径都可以)模式扩展(?单个字符,*任意个字符,[]字符选集),返回一个匹配文件名(带前缀路径)组成的列表.

  • os.listdir():接受路径目录名,返回该目录下所有文件名(不带路径前缀)列表.

    • golb和walk都都通过调用listdir实现.

    • 如果文件名有特殊字符,可以通过传入字节字符串(b’…').

    • 文件名编码:os.getfilesystemencoding()

遍历目录树

  • os.walk():接受一个路径目录名,返回一个生成器,为该目录及其每一个递归子目录下产生一个包括(当前目录名称,子目录列表,文件列表)的三元组.用topdown=False参数可以自底向上遍历.删除返回的元祖里子目录列表中的名称,可以对目录树的枝干进行修剪.

并行系统工具

进程分支

os.fork():

  • 启动新的并行子进程(内存中原来进程的一个副本),在子进程中返回0,在父进程中返回pid

  • os.getpid()获得当前进程的pid

  • 全局对象在子进程开始是有相同的值,全局内存是被复制而不是共享,只改变自己的副本.

  • 子进程可以通过显是的调用os._exit(0)退出

fork和exec组合:

  • os.execlp():用一个全新的程序覆盖执行原来的程序,通过fork启动子进程再exec覆盖,开启一个与原程序并行运行的新程序,但pid不变

  • exec系列:

    • execv(program, commandlinesequence):参数为可执行程序的名称(完整路径),命令行参数字符串组成的列表或元组(在shell中输入的命令)

    • execl(program, argv0, argv1, …, argvN):参数形式传入命令行参数

    • excelp,excevp:用系统搜索路径定位可执行程序(不需要完整路径)

    • execve,execle:在最后添加一个字典参数,包含发送给程序的shell环境变量

    • execvpe,execlpe:使用搜索路径并且接受shell环境变量字典参数

    • 例子:os.execlp('python', 'python', 'child.py', str(parm))

线程

在同一个进程中,和程序的其他部分并行的调用函数(或其他可调用对象),常用于非阻塞的输入调用和GUI中长时间的任务.

特点:

  • 相对与分支进程开销小,进程适合计算密集型操作,线程适合IO密集型操作

  • 共享全局内存(对象和命名空间4),要仔细控制共享项目的访问权

  • 全局解释器锁(GIL):同一时间只有一个线程运行python代码,不能利用多核

_thread模块:

  • _thread.start_new_thread():

    • 开始一个新线程,接受一个函数(或其他可调用对象,如lambda函数,对象方法)和一个参数元组,返回一个无用值.

    • 线程在其运行的函数返回后退出,或主线程退出后随之退出.

    • 线程出现异常不影响程序其他部分的运行.

  • 同步访问共享对象和名称:

    • 锁:要修改一个共享对象,线程 需要获得一把锁,然后修改,之后释放锁给其他线程获取.确保任何时间只有一个线程持有锁.

    • _thread.allocate_lock():创建全局锁对象,_thread.exit():线程退出

    • 全局锁对象的acquire()方法获取锁,release()方法释放锁,之间是需要互斥的操作

    • locked()方法检测锁是否被获取,被获取返回true

  • 等待派生线程退出(线程通信)

    • 锁列表:创建一个锁列表,通过为每一个子线程结尾获取一把锁,判断每一把锁是否都被获取,则说明子线程都已经结束,就可以退出主线程.

    • 整数列表:创建一个列表,每一个子线程结尾都改变其值,通过判断是否每一个值都改变了,得知子线程是否都结束.可在主线程中用time.sleep()暂停等待子线程结束

  • 上下文管理

    • with mutex(全局锁对象):自动上锁和解锁

threading模块:

  • 基于对象和类的较高层面的接口,内部使用_thread实现

  • 可以通过继承threading.Thread对象定制带有状态(初始化方法)和run()行为(提供线程逻辑业务)的线程类.

  • threading.Lock():创建全局锁对象.全局锁对象的acquire()方法获取锁,release()方法释放锁,之间是需要互斥的操作

  • 线程类对象的start()方法:在线程中运行run方法.join():等待直到线程退出.如果任何一个派生线程(守护线程除外)还在运行中,程序不会退出.线程类对象的daemon属性=True可设为守护线程

  • threading.Thread对象接受传给target参数的调用对象和任意传给args参数(默认(),代表无)

  • threading模块还有Semaphore, Condition和Event等

queue模块:

  • 线程程序由一系列消费者程序和一系列生产者程序组成,通过将数据存入一个共享的队列或从中取出来进行通信,保证线程安全.队列同步化的是数据传递,某些操作仍然需要为其他目的而使用锁.

  • queue.Queue(maxsize = 0)创建一个队列对象.maxsize可限制队列大小,默认无限制.

  • queue.LifoQueue(maxsize = 0)创建一个栈队列对象

  • queue.PriorityQueue(maxsize = 0)创建一个优先级队列.典型加入的元素是一个元祖(优先级, 数据),优先级数越小,级别越高

  • queue.deque(maxsize = 0)创建一个双线队列

  • queue.empty异常,queue.Full异常:队列空取,队列满存产生的异常

  • 队列对象(例如Queue)的put(item, block=True, timeout=None)方法向队列中存入数据.如果满,blocking = False 直接报 Full异常。如果blocking = True,就是等一会,timeout必须为 0 或正数。None为一直等下去,0为不等,正数n为等待n秒还不能存入,报Full异常

  • Queue.get()方法从队列中取出数据,参数类似put

  • Queue.empty(),Queue.full():判断队列是否为空,为满

  • Queue.task_done():表示队列中某个元素被消费进程使用,消费结束发送的信息

  • Queue.join():一直阻塞直到队列中的所有元素都被取出和执行

GUI与线程:

  • GUI程序一般采用主GUI线程加一到多个长时间运行的生产者线程,主线程更新界面,每个生产者线程执行一个长时间任务.所有线程共享一个队列,非GUI线程显示结果,GUI线程消耗它们.