[Python学习日记-52] Python 中的 copy 模块 —— shutil
[Python学习日记-52] Python 中的 copy 模块 —— shutil
简介
shutil 模块
简介
在前面的学习当中,我们学习了如何在 Python 中创建文件,这个时候我们基本已经有了写程序的能力了,而有的时候我们也想使用 Python 来对文件进行更多的操作,例如修改它的权限信息、存储位置等,同时在传递程序或者提供程序下载时,我们更希望的是程序能越小越好,这样对于用户传输的带宽要求、等待时间等都会更友好,这就要用到 shutil 模块了,下面我们一起来看一看吧。
shutil 模块
该模块是高级的文件、目录、压缩包的处理模块
一、copyfileobj()
将文件内容拷贝到另一个文件中,语法如下
shutil.copyfileobj(src_file_obj, dest_file_obj, length)
参数说明:
演示代码如下
import shutil
shutil.copyfileobj(open('game.json','r'),open('game_new.json','w'))
代码效果如下:
二、copyfile()
拷贝整个文件,如果目标文件不存在则会直接创建,语法如下
shutil.copyfile(src, dst, *, follow_symlinks=True)
参数说明:
如果复制操作成功,则返回目标文件的路径,演示代码如下
import shutil
shutil.copyfile("game.json","game1.json") # 目标文件无需存在
代码效果如下:
三、copymode()
仅拷贝权限,而内容、组、用户均不变,语法如下
shutil.copymode(src, dst)
参数说明:
演示代码如下
import shutil
shutil.copymode("game.json","game1.json") # 目标文件必须存在
该模块对于 Linux 系统中的权限操作比较好使,对于 Windows 的权限操作该模块实测并无效果。
四、copystat()
仅拷贝状态信息,包括:mode bits,atime,mtime,flags,语法如下
shutil.copystat(src, dst, *, follow_symlinks=True)
参数说明:
该函数用于将 src 的文件权限和状态信息复制到 dst 中。它会复制文件的权限位,最后访问时间,最后修改时间和用户 id/group_id,演示代码如下
import shutil
shutil.copystat("game.json","game1.json") # 目标文件必须存在
代码效果如下:
在 Windows 下只能复制修改时间,该模块与 copymode() 一样,在 Linux 系统中的权限操作比较好使。
五、copy()
拷贝文件或目录和权限,语法如下
shutil.copy(src, dst, *, follow_symlinks=True)
参数说明:
该方法是将源文件复制到目标文件,并返回目标文件的路径,演示代码如下
import shutil
print(shutil.copy("game.json","game1.json"))
代码输出如下:
但它并不能复制整个目录,如果需要的话可以使用 copytree() 方法。
六、copy2()
拷贝文件或目录和状态信息,语法如下
shutil.copy2(src, dst, *, follow_symlinks=True)
参数说明:
演示代码如下
import shutil
print(shutil.copy2("game.json","game1.json"))
代码输出如下:
七、ignore_patterns() & copytree()
copytree() 方法可递归的去拷贝目录,通常会配合 ignore_patterns() 方法使用,可以对不想拷贝的文件进行过滤,语法如下
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False)
shutil.ignore_patterns(*patterns)
copytree() 的参数说明:
ignore_patterns() 的参数说明:
ignore_patterns() 会根据这些模式创建一个用于过滤名称的函数,并将其返回。下面我们将结合这两种方法来进行演示,演示代码如下
import shutil
shutil.copytree("jove_test_module", "jove_test_module_copytree",ignore=shutil.ignore_patterns("*.md","top*"))
代码效果如下:
八、rmtree()
递归的去删除整个目录,语法如下
shutil.rmtree(path, ignore_errors=False, οnerrοr=None)
参数说明:
演示代码如下
import shutil
# 删除目录及其下的所有文件和子目录
shutil.rmtree('/path/to/directory')
# 忽略错误,即使目录不存在也不会抛出异常
shutil.rmtree('/path/to/directory', ignore_errors=True)
# 自定义错误处理函数
def handle_error(func, path, exc_info):
print(f"Error: {path} - {exc_info[1]}")
shutil.rmtree('/path/to/directory', onerror=handle_error)
注意:使用该函数要格外小心,因为它会删除整个目录,包括目录下的所有文件和子目录,且无法恢复!
九、move()
递归的去移动整个目录,它类似 Linux 中的 mv 命令,其实就是重命名,语法如下
shutil.move(src, dst, copy_function=shutil.copy2)
参数说明:
演示代码如下
import shutil
# 移动文件
shutil.move('file.txt', 'new_location/file.txt') # 目标路径的目录要先创建好
# 移动目录
shutil.move('dir', 'new_location/dir') # 目标路径的目录要先创建好
代码效果如下:
十、make_archive()
创建压缩包并返回文件路径,例如:zip、tar,语法如下
shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=False, owner=None, group=None, logger=None)
参数说明:
演示代码如下
import shutil
# 将 .\new_location 下的文件打包放置当前程序目录
ret = shutil.make_archive("data_bak","gztar",root_dir=r".\new_location")
# 将 .\new_location 下的文件打包放置 C:\Users\Administrator\Desktop\tmp\ 目录
ret = shutil.make_archive(r"C:\Users\Administrator\Desktop\tmp\data_bak","gztar",root_dir=r".\new_location")
代码效果如下:
其实 shutil 对压缩包的处理是调用 zipfile 和 tarfile 两个模块来进行的,下面我们也来讲一讲这两个模块的用法。
zipfile 压缩和解压缩:
import zipfile
# 压缩
z = zipfile.ZipFile(r"C:\Users\Administrator\Desktop\test_compress.zip","w")
z.write("jove_test_module")
z.write("game.json")
z.close()
# 解压缩
z = zipfile.ZipFile(r"C:\Users\Administrator\Desktop\test_compress.zip","r")
z.extractall(path=r"C:\Users\Administrator\Desktop\tmp")
z.close()
代码效果如下:
但是会发现一个问题,ZipFile 并没有把整个目录都拷贝过来,只是把最表层的目录拷贝了过去而已,如图所示
这与我们的设想不太一样,这个时候我们可以使用 os 模块中的 walk() 方法,来把整个目录拷贝过去,我们先来看看 os.walk() 到底如何使用先,代码如下
import os
for i in os.walk("./jove_test_module"):
print(i)
代码输出如下:
输出当中每一次输出都是一个元组,而元组包含三个元素,第一个是 root 目录,第二个是 root 目录下的目录,第三个是 root 目录下的文件。分析完输出的数据后就可以根据 os.walk() 来编写程序了,代码如下
import os
import zipfile
z = zipfile.ZipFile(r'C:\Users\Administrator\Desktop\test_compress.zip','w')
filelist = []
for root, dirs, files in os.walk(r"jove_test_module"):
if not files:
if not dirs:
filelist.append(root)
else:
for name in files:
filelist.append(os.path.join(root,name))
for file in filelist:
print(file)
z.write(file)
z.close()
代码效果与输出如下:
tarfile 压缩和解压缩:
将目标文件打包为 tar 包,而且打包为 tar 包是只打包不压缩的,代码如下
import tarfile
# 压缩
t = tarfile.open(r'C:\Users\Administrator\Desktop\test_compress.tar','w')
t.add("jove_test_module")
t.add(r"jove_test_module\setup.py")
t.add("game.json")
t.close()
# 解压缩
t = tarfile.open(r'C:\Users\Administrator\Desktop\test_compress.tar','r')
t.extractall("./tmp",filter="fully_trusted") # 解压整个文件
t.extract("jove_test_module/setup.py","./tmp/jove_module",filter="fully_trusted") # 解压单个文件
t.close()
代码效果如下:
注意:tarfile 模块存在限制,为了避免解压时会出现覆盖其他文件的错误操作,这就导致了可能会出现一下报错:DeprecationWarning: Python 3.14 will, by default, filter extracted tar archives and reject files or modify their metadata. Use the filter argument to control this behavior.
t.extractall("./tmp"),这就是由于 tarfile 的安全限制导致的,这个时候我们需要使用 filter="fully_trusted" 来放开限制
filter 参数详细可参见官方文档:tarfile — Read and write tar archive files — Python 3.12.7 documentation
作者:JoveZou