python常用模块——subprocess,shutil,selectors模块

subprocess

subprocess模块允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等

(官方文档),在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能。

函数描述
subprocess.run()Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
subprocess.call()执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
subprocess.check_call()Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。
subprocess.check_output()Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
subprocess.getoutput(cmd)接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
subprocess.getstatusoutput(cmd)执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。

run方法的使用方法

def run(*popenargs,
        input=None, capture_output=False, timeout=None, check=False, **kwargs):
    ...
    return CompletedProcess(process.args, retcode, stdout, stderr)

参数

  1. popenargs: 要执行的shell命令,默认应该是一个字符串序列,如[‘df’, ‘-Th’]或(‘df’, ‘-Th’),也可以是一个字符串,如’df -Th’,但是此时需要把shell参数的值置为True。
  2. shell: 如果shell为True,那么指定的命令将通过shell执行。
  3. check: 如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常,且该异常对象会包含参数、退出状态码、以及stdout和stderr。
  4. stdout, stderr:input: 该参数是传递给Popen.communicate(),通常该参数的值必须是一个字节序列,如果universal_newlines=True,则其值应该是一个字符串。
  5. universal_newlines: 该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时stdout和stderr的输出是字节序列;当该参数的值设置为True时,stdout和stderr的输出是字符串。

与其他方法的区别

subprocess 模块提供了多种方法来执行外部命令,每个方法都有其特定的用途和行为。以下是 subprocess.run()subprocess.call()subprocess.check_call()subprocess.check_output() 这四个方法的详细比较:

1. subprocess.run()
  • 引入版本: Python 3.5
  • 返回值: 返回一个 subprocess.CompletedProcess 对象,包含了执行命令的结果信息,如返回码、标准输出和标准错误。
  • 参数:
    • args: 要执行的命令及其参数,可以是一个字符串或一个列表。
    • capture_output: 如果为 True,则捕获标准输出和标准错误。
    • check: 如果为 True,则在命令返回非零退出状态时引发 subprocess.CalledProcessError 异常。
    • timeout: 指定命令的超时时间,超时后会引发 subprocess.TimeoutExpired 异常。
    • 其他参数如 stdin, stdout, stderr, shell, cwd, env 等。

示例:

import subprocess

# 执行命令并捕获输出
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout)

# 执行命令并检查返回码
subprocess.run(['ls', '-l'], check=True)
2. subprocess.call()
  • 引入版本: Python 2.4
  • 返回值: 返回命令的退出状态码(整数)。
  • 参数:
    • args: 要执行的命令及其参数,可以是一个字符串或一个列表。
    • 其他参数如 stdin, stdout, stderr, shell, cwd, env 等。

示例:

import subprocess

# 执行命令并获取返回码
return_code = subprocess.call(['ls', '-l'])
print(return_code)
3. subprocess.check_call()
  • 引入版本: Python 2.5
  • 返回值: 返回命令的退出状态码(整数),如果命令成功执行。
  • 参数:
    • args: 要执行的命令及其参数,可以是一个字符串或一个列表。
    • 其他参数如 stdin, stdout, stderr, shell, cwd, env 等。
  • 行为: 如果命令返回非零退出状态,则引发 subprocess.CalledProcessError 异常。

示例:

import subprocess

# 执行命令并检查返回码
subprocess.check_call(['ls', '-l'])
4. subprocess.check_output()
  • 引入版本: Python 2.7
  • 返回值: 返回命令的标准输出(字节字符串)。
  • 参数:
    • args: 要执行的命令及其参数,可以是一个字符串或一个列表。
    • 其他参数如 stdin, stderr, shell, cwd, env 等。
  • 行为: 如果命令返回非零退出状态,则引发 subprocess.CalledProcessError 异常。

示例:

import subprocess

# 执行命令并获取输出
output = subprocess.check_output(['ls', '-l'])
print(output.decode('utf-8'))
主要区别
  1. 返回值:

    • subprocess.run(): 返回 subprocess.CompletedProcess 对象,包含了更多的执行结果信息。
    • subprocess.call(): 返回命令的退出状态码(整数)。
    • subprocess.check_call(): 返回命令的退出状态码(整数),如果命令成功执行。
    • subprocess.check_output(): 返回命令的标准输出(字节字符串)。
  2. 异常处理:

    • subprocess.run(): 可以通过 check=True 参数在命令失败时引发异常。
    • subprocess.call(): 不会引发异常,只返回退出状态码。
    • subprocess.check_call(): 在命令返回非零退出状态时引发 subprocess.CalledProcessError 异常。
    • subprocess.check_output(): 在命令返回非零退出状态时引发 subprocess.CalledProcessError 异常。
  3. 功能丰富性:

    • subprocess.run(): 提供了更多的参数和功能,如 capture_output, check, timeout 等。
    • subprocess.call(), subprocess.check_call(), subprocess.check_output(): 功能相对简单,专注于特定的任务。
选择建议
  • 如果你需要捕获命令的输出或处理命令失败的情况,建议使用 subprocess.run()subprocess.check_output()
  • 如果你只需要执行命令并获取其退出状态码,可以使用 subprocess.call()
  • 如果你需要确保命令成功执行并在失败时引发异常,可以使用 subprocess.check_call()

总的来说,subprocess.run() 是一个更现代和功能更强大的方法,适用于大多数场景。

CompletedProcess

表示的是一个已结束进程的状态信息

  1. args: 用于加载该进程的参数,这可能是一个列表或一个字符串
    2/ returncode: 子进程的退出状态码。通常情况下,退出状态码为0则表示进程成功运行了;一个负值-N表示这个子进程被信号N终止了
  2. stdout: 从子进程捕获的stdout。这通常是一个字节序列,如果run()函数被调用时指定universal_newlines=True,则该属性值是一个字符串。如果run()函数被调用时指定stderr=subprocess.STDOUT,那么stdout和stderr将会被整合到这一个属性中,且stderr将会为None
  3. stderr: 从子进程捕获的stderr。它的值与stdout一样,是一个字节序列或一个字符串。如果stderr没有被捕获的话,它的值就为None
  4. check_returncode(): 如果returncode是一个非0值,则该方法会抛出一个CalledProcessError异常。

subprocess.Popen类

@overload
        def __new__(
            cls,
            args: _CMD,
            bufsize: int = ...,
            executable: Optional[AnyPath] = ...,
            stdin: Optional[_FILE] = ...,
            stdout: Optional[_FILE] = ...,
            stderr: Optional[_FILE] = ...,
            preexec_fn: Optional[Callable[[], Any]] = ...,
            close_fds: bool = ...,
            shell: bool = ...,
            cwd: Optional[AnyPath] = ...,
            env: Optional[_ENV] = ...,
            universal_newlines: bool = ...,
            startupinfo: Optional[Any] = ...,
            creationflags: int = ...,
            restore_signals: bool = ...,
            start_new_session: bool = ...,
            pass_fds: Any = ...,
            *,
            text: Optional[bool] = ...,
            encoding: str,
            errors: Optional[str] = ...,
        ) -> Popen[str]: ...

参数

  1. args: 要执行的shell命令,可以是字符串,也可以是命令各个参数组成的序列。当该参数的值是一个字符串时,该命令的解释过程是与平台相关的,因此通常建议将args参数作为一个序列传递。
  2. bufsize: 指定缓存策略,0表示不缓冲,1表示行缓冲,其他大于1的数字表示缓冲区大小,负数表示使用系统默认缓冲策略。
  3. stdin, stdout, stderr: 分别表示程序标准输入、输出、错误句柄。
  4. preexec_fn: 用于指定一个将在子进程运行之前被调用的可执行对象,只在Unix平台下有效。
  5. close_fds: 如果该参数的值为True,则除了0,1和2之外的所有文件描述符都将会在子进程执行之前被关闭。
  6. shell: 该参数用于标识是否使用shell作为要执行的程序,如果shell值为True,则建议将args参数作为一个字符串传递而不要作为一个序列传递。
  7. cwd: 如果该参数值不是None,则该函数将会在执行这个子进程之前改变当前工作目录。
  8. env: 用于指定子进程的环境变量,如果env=None,那么子进程的环境变量将从父进程中继承。如果env!=None,它的值必须是一个映射对象。
  9. universal_newlines: 如果该参数值为True,则该文件对象的stdin,stdout和stderr将会作为文本流被打开,否则他们将会被作为二进制流被打开。
  10. startupinfo和creationflags: 这两个参数只在Windows下有效,它们将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如主窗口的外观,进程优先级等。

Popen 对象方法

with subprocess.Popen(["pwd"], shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) as process:
    pass
  1. poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
  2. wait(timeout): 等待子进程终止。
  3. communicate(input,timeout): 和子进程交互,发送和读取数据。
  4. send_signal(singnal): 发送信号到子进程 。
  5. terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。
  6. kill(): 杀死子进程。发送 SIGKILL 信号到子进程。
>>> obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> out,err = obj.communicate(input="print(1)")
>>> print(out)
1

信号

SIGHUP 	终止进程 终端线路挂断
SIGINT 	终止进程 中断进程
SIGQUIT 	建立CORE文件终止进程,并且生成core文件
SIGILL 		建立CORE文件 非法指令
SIGTRAP 	建立CORE文件 跟踪自陷
SIGBUS 		建立CORE文件 总线错误
SIGSEGV 	建立CORE文件 段非法错误
SIGFPE 		建立CORE文件 浮点异常
SIGIOT 		建立CORE文件 执行I/O自陷
SIGKILL 		终止进程 杀死进程
SIGPIPE 		终止进程 向一个没有读进程的管道写数据
SIGALARM 	终止进程 计时器到时
SIGTERM 		终止进程 软件终止信号
SIGSTOP		停止进程     非终端来的停止信号
SIGTSTP   		停止进程     终端来的停止信号
SIGCONT   		忽略信号     继续执行一个停止的进程
SIGURG   		忽略信号     I/O紧急信号
SIGIO     			忽略信号     描述符上可以进行I/O
SIGCHLD   		忽略信号     当子进程停止或退出时通知父进程
SIGTTOU   		停止进程     后台进程写终端
SIGTTIN   		停止进程     后台进程读终端
SIGXGPU   		终止进程     CPU时限超时
SIGXFSZ   		终止进程     文件长度过长
SIGWINCH   	忽略信号     窗口大小发生变化
SIGPROF   		终止进程     统计分布图用计时器到时
SIGUSR1   		终止进程     用户定义信号1
SIGUSR2   		终止进程     用户定义信号2
SIGVTALRM 终止进程     虚拟计时器到时

selectors

IO复用

  1. 复用:也就是共用的意思,在通信领域中为了充分利用网络连接的物理介质,往往在同一条网络链路上采用时分复用或频分复用的技术使其在同一链路上传输多路信号,即公用某个“介质”来尽可能多的做同一类(性质)的事
  2. IO复用:客户端发来的请求服务端会产生一个进程来对其进行服务,每当来一个客户请求就产生一个进程来服务,然而进程不可能无限制的产生,因此为了解决大量客户端访问的问题,引入了IO复用技术,即:一个进程可以同时对多个客户请求进行服务。
  3. 也就是说IO复用的“介质”是进程(准确的说复用的是select和poll,因为进程也是靠调用select和poll来实现的),复用一个进程(select和poll)来对多个IO进行服务,虽然客户端发来的IO是并发的但是IO所需的读写数据多数情况下是没有准备好的,因此就可以利用一个函数(select和poll)来监听IO所需的这些数据的状态,一旦IO有数据可以进行读写了,进程就来对这样的IO进行服务。

多路复用的API

  1. select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。

  2. select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    
    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    

select

  1. 第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位图,其大小限制为__FD_SETSIZE(1024),位图的每一位代表其对应的描述符是否需要被检查。

  2. 第二三四参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件,所以每次调用select前都需要重新初始化fdset。

  3. timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。

  4. select的调用步骤如下:

    (1)使用copy_from_user从用户空间拷贝fdset到内核空间

    (2)注册回调函数__pollwait

    (3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)

    (4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数。

    (5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll 来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数 据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。

    (6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值。

    (7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是 current)进入睡眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(schedule_timeout 指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd。

    (8)把fd_set从内核空间拷贝到用户空间。

  5. 总结下select的几大缺点:

    (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

    (2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

    (3)select支持的文件描述符数量太小了,默认是1024

poll

通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。

poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。

epoll

直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果没有采取行动,那么它将不会再次告知,这种方式称为边缘触发)

理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

晕了(((φ(◎ロ◎;)φ)))

select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函 数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注 册要监听的事件类型;epoll_wait则是等待事件的产生。

对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定 EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝 一次。

对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在 epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调 函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用 schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子, 在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

总结:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用 epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在 epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的 时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间,这就是回调机制带来的性能提升。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要 一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内 部定义的等待队列),这也能节省不少的开销。

selectors实现io多路复用

这三种IO多路复用模型在不同的平台有着不同的支持,而epoll在windows下就不支持,selectors模块可以自动选择不同的Api

from socket import *
import selectors

sel = selectors.DefaultSelector()
server = socket()
server.bind(('127.0.0.1', 5000))
server.listen(5)
# 设置socket的接口为非阻塞
server.setblocking(False)


def accept(server_fileobj, mask):
    conn, addr = server_fileobj.accept()
    sel.register(conn, selectors.EVENT_READ, read)


def read(conn, mask):
    try:
        data = conn.recv(1024)
        if not data:
            print('closing', conn)
            sel.unregister(conn)
            conn.close()
            return
        conn.send(data.upper() + b'_SB')
    except Exception:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()


# 相当于在select的读列表里append了一个文件句柄server,并且绑定了一个回调函数accept
sel.register(server, selectors.EVENT_READ, accept)

while True:
    # 检测所有的fileobj,是否有完成wait data的
    events = sel.select()

    for sel_obj, mask in events:
        callback = sel_obj.data
        print(callback)  # accept 或 read
        callback(sel_obj.fileobj, mask)

shutil

shutil是高级的 文件、文件夹、压缩包 处理模块。

shutil.copyfileobj(fsrc, fdst[, length])

将文件内容拷贝到另一个文件中

import shutil 
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)

拷贝文件

shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在

shutil.copymode(src, dst)

仅拷贝权限。内容、组、用户均不变

shutil.copymode('f1.log', 'f2.log') #目标文件必须存在

shutil.copystat(src, dst)

仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

shutil.copystat('f1.log', 'f2.log') #目标文件必须存在

shutil.copy(src, dst)

拷贝文件和权限

import shutil 
shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)

拷贝文件和状态信息

import shutil 
shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns),shutil.copytree(src, dst, symlinks=False, ignore=None)

递归的去拷贝文件夹

import shutil 
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除 

shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件

shutil.rmtree(path[, ignore_errors[, onerror]])

递归的去删除文件

import shutil 
shutil.rmtree('folder1')

shutil.move(src, dst)

递归的去移动文件,它类似mv命令,其实就是重命名。

import shutil  
shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,…)

创建压缩包并返回文件路径,例如:zip、tar

base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak                       =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象

#将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data') 
#将 /data下的文件打包放置 /tmp/目录
import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data') 

shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:

import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()

import tarfile

# 压缩
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()


# 解压
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()

unpack_archive(filename, extract_dir=None, format=None)

import shutil

# 解压
shutil.unpack_archive("要解压的压缩文件", "解压后文件存在哪个位置")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Generalzy

文章对您有帮助,倍感荣幸

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值