【问题标题】:Copy file with pathlib in Python在 Python 中使用 pathlib 复制文件
【发布时间】:2016-02-11 02:10:56
【问题描述】:

我尝试使用pathlib 复制文件

import pathlib
import shutil

my_file=pathlib.Path('/etc/hosts')
to_file=pathlib.Path('/tmp/foo')
shutil.copy(my_file, to_file)

我得到了这个例外:

/home/foo_egs_d/bin/python /home/foo_egs_d/src/test-pathlib-copy.py
Traceback (most recent call last):
  File "/home/foo_egs_d/src/test-pathlib-copy.py", line 6, in <module>
    shutil.copy(my_file, to_file)
  File "/usr/lib/python2.7/shutil.py", line 117, in copy
    if os.path.isdir(dst):
  File "/home/foo_egs_d/lib/python2.7/genericpath.py", line 41, in isdir
    st = os.stat(s)
TypeError: coercing to Unicode: need string or buffer, PosixPath found

Process finished with exit code

...如何在 Python 2.7 中使用 pathlib 复制文件?

【问题讨论】:

  • 这在 Python 3.6 上不会出错
  • @Anthon 我们使用 Python 2.7。

标签: python file copy python-2.x pathlib


【解决方案1】:

shutil 在 Python 3.6 中如何转换为接受 pathlib.Path 对象

in this answer 所述,Python 3.6 中的 shutil 可以采用 pathlib.Path 对象。

因为这感觉很神奇,所以我决定研究一下它是如何实现的,看看我是否能够在我自己的类中重用这个魔法。

改进是 PEP 519 的结果。

这概括了许多 stdlib 功能,因此文档没有得到一致更新,包括大部分 shutil,截至 3.7 only documents support in a single function。欢迎体验动态输入的乐趣。

在文档中,stlib 链接到 glossary for "path-like objects"

表示文件系统路径的对象。类路径对象是表示路径的 str 或字节对象,或者是实现 os.PathLike 协议的对象。支持 os.PathLike 协议的对象可以通过调用 os.fspath() 函数转换为 str 或 bytes 文件系统路径; os.fsdecode() 和 os.fsencode() 可分别用于保证 str 或 bytes 结果。由 PEP 519 引入。

然后链接到os.PathLike的文档:

代表文件系统路径的对象的抽象基类,例如pathlib.PurePath.

3.6 版中的新功能。

abstractmethod __fspath__()

返回对象的文件系统路径表示。

该方法应该只返回一个 str 或 bytes 对象,优先考虑 str。

关键的实现提交似乎是:

如果你想实现自己的类路径类,你可以这样做:

#!/usr/bin/env python3

class MyPath:
    def __init__(self, path):
        self.path = path
    def __fspath__(self):
        return self.path

with open(MyPath('f'), 'w'):
    pass

在 Python 3.6.7、Ubuntu 18.10 中测试。

【讨论】:

    【解决方案2】:

    您可以使用pathlib 重命名方法代替shutil.move()

    import pathlib
    
    my_file = pathlib.Path('/etc/hosts')
    to_file = pathlib.Path('/tmp/foo')
    my_file.rename(to_file)
    

    【讨论】:

    • 使用shutil.copy(),您将拥有两个副本:原始副本和目标副本。如果您使用Path.rename,您的原件就没有了,所以这与操作人员想要的完全无关。
    【解决方案3】:

    使用shutil.copy

    import pathlib
    import shutil
    
    my_file = pathlib.Path('/etc/hosts')
    to_file = pathlib.Path('/tmp/foo')
    
    shutil.copy(str(my_file), str(to_file))  # For Python <= 3.7.
    shutil.copy(my_file, to_file)  # For Python 3.8+.
    

    问题是pathlib.Path 如果您使用的是 Unix/Linux,请创建一个 PosixPath 对象,如果您使用的是 Microsoft Windows,请创建 WindowsPath

    对于旧版本的 Python,shutil.copy 需要一个字符串作为其参数。对于他们,请在此处使用str 函数。

    【讨论】:

    • @guettli 实际上,如果您在 Windows 上使用 my_file.as_posix(),它将返回 C:/etc/hosts。所以这没关系。
    • 不要把这个当作个人的:我认为 pathlib 是为了让事情变得更容易。我想我会像以前一样坚持使用普通的旧字符串。
    • 我还希望 Pathlib 能够复制文件,因为它可以移动/重命名和取消链接/删除文件。
    • @AndrewWagner 不同之处在于,如果我不得不猜测的话,移动和删除文件是纯粹的文件系统操作,您只需更新文件系统元数据。复制实际上涉及写出新数据,所以也许 pathlib 人们认为它超出了范围。
    • 如果有人想知道为什么 shutil.move 在 Python
    【解决方案4】:

    您可能会使用 pathlib3x - 它提供了最新的(在撰写此答案 Python 3.10.a0 之日)Python 3.6 或更高版本的 Python pathlib 的反向移植,以及一些附加功能,如 copycopy2、等等……

    $> python -m pip install pathlib3x
    $> python
    >>> import pathlib3x as pathlib
    >>> my_file = pathlib.Path('/etc/hosts')
    >>> to_file = pathlib.Path('/tmp/foo')
    >>> my_file.copy(to_file)
    

    您可以在githubPyPi 上找到它


    免责声明:我是 pathlib3x 库的作者。

    【讨论】:

      【解决方案5】:

      从 Python 3.5 开始,无需导入 shutil,您可以这样做:

      from pathlib import Path
      
      dest = Path('dest')
      src = Path('src')
      dest.write_bytes(src.read_bytes()) #for binary files
      dest.write_text(src.read_text()) #for text files
      

      对于 Python 2.7,pathlib2 提供了 read_bytesread_textwrite_byteswrite_text 方法。

      文件会被加载到内存中,所以这种方法不适合大于机器可用内存的文件。

      根据 cmets,可以使用 write_bytesread_bytes 来复制文本文件,但如果您需要在复制时处理编码,write_textread_text 将具有两个额外参数的优势:

      • encoding 是用于对文件进行解码或编码的编码名称
      • errors 是一个可选字符串,用于指定如何处理编码和解码错误

      它们都与open()中的含义相同。

      【讨论】:

      • 有没有办法用 Python 2.7 做到这一点?
      • Python 2.7 有一些东西,即pathlib2,但我还没有尝试过。 pypi.python.org/pypi/pathlib2read_byteswrite_bytes 方法在源代码中,所以我认为它们可以工作。
      • @GeorgeSovetov 谢谢我将它添加到答案中。
      • 使用这种方法,足够大的文件会导致 Python 崩溃并返回 MemoryErrorshutil.copy 没有这个问题,因为它使用shutil.copyfileobj 将较大的文件缓冲成较小的块。
      • 这会复制文件的内容,但不会复制文件权限、所有权、访问策略或其他文件元数据。 OP 的评论实际上是说他/她想要“复制文件”,而不仅仅是它包含的数据。
      【解决方案6】:

      shutil.copy() 不起作用的原因是您没有使用最新的 Python,Python 3.6 shutil.copy() 可以 处理 Path 对象(或其子类)。对于旧版本的 Python,这会引发错误,因为 shutil 的那些实现需要 copy 的字符串参数,而不是 pathlib.Path 类型的参数。

      你真正想写的是:

      my_file.copy(to_file)
      

      你可以继承 Path 来包含这样的方法,并适应 my_file 的创建。我发现在现有的pathlib.Path 上移植/monkey-patch/duck-punch 更容易

      from pathlib import Path
      
      
      def _copy(self, target):
          import shutil
          assert self.is_file()
          shutil.copy(str(self), str(target))  # str() only there for Python < (3, 6)
      
      Path.copy = _copy
      

      您可以将此代码放在您喜欢的任何位置,只要它在任何Path 实例上调用.copy 方法之前执行。 .copy() 的参数可以是文件或目录。

      【讨论】:

      • AFAIK 这被称为猴子补丁。为什么 pathlib 不提供这个?
      • 我会说 pathlib 不提供此功能,因为这不是它的用途 - 就像 os.path 不适合文件处理本身一样。据我所知,该模块的功能与文件处理和文件元数据有关,但与文件处理无关。
      • @mr.zog 我认为您的目标受众不会在您输入姓名时收到通知您写了评论。您应该咨询help on formatting of comments,其中明确声明使用@ 字符
      • @Bram Vanroy “模块的功能是关于文件处理和文件的元数据,而不是关于文件处理”有点矛盾。你打算传达什么?
      • 虽然 Path 主要是用来表示文件路径的,但它也提供了 mkdir() 和 exists() 等,所以很自然地期望它会做很多 shutil 所做的事情。适当的关注点分离可能会有路径和文件和文件夹,文件/文件夹是从路径创建的,文件有 getPath() 等。然后,一旦你有一个文件或文件夹,很明显有 copy() 是有意义的, copytree() 等在这些对象上而不是在路径上。顺便说一句,我在任何地方都使用 Path。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-27
      • 2022-07-27
      • 2018-01-25
      • 1970-01-01
      • 2021-02-13
      • 1970-01-01
      • 2019-06-18
      相关资源
      最近更新 更多