子类“pathlib.Path”失败

Subclass `pathlib.Path` fails

我想增强 class pathlib.Path 但上面的简单示例不起作用。

from pathlib import Path

class PPath(Path):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

test = PPath("dir", "test.txt")

这是我收到的错误消息。

Traceback (most recent call last):
  File "/Users/projetmbc/test.py", line 14, in <module>
    test = PPath("dir", "test.txt")
  File "/anaconda/lib/python3.4/pathlib.py", line 907, in __new__
    self = cls._from_parts(args, init=False)
  File "/anaconda/lib/python3.4/pathlib.py", line 589, in _from_parts
    drv, root, parts = self._parse_args(args)
  File "/anaconda/lib/python3.4/pathlib.py", line 582, in _parse_args
    return cls._flavour.parse_parts(parts)
AttributeError: type object 'PPath' has no attribute '_flavour'

我做错了什么?

HerePathclass的定义。它做了一些相当聪明的事情。它不是直接从其 __new__() 返回 Path 的实例,而是 returns 子 class 的实例,但 如果它被直接调用为 Path()(而不是作为子class)。

否则,它应该通过 WindowsPath()PosixPath() 调用,它们都通过多重继承提供 _flavour class 属性。您还必须在 subclassing 时提供此属性。您可能需要实例化 and/or subclass the _Flavour class 来执行此操作。这不是 API 的受支持部分,因此您的代码可能会在 Python.

的未来版本中中断

TL;DR: This idea is fraught with peril, and I fear that my answers to your questions will be interpreted as approval rather than reluctant assistance.

根据凯文的观察,这里有一个简单的方法。

class PPath():
    def __init__(self, *args, **kwargs):
        self.path = Path(*args, **kwargs)

然后我需要使用一个技巧来自动将所有路径的方法绑定到我的 PPpath class。我认为这样做会很有趣。

备注

在对 Python 开发者进行一些讨论后,我打开了 a bug track here。列表。

临时解决方案

很抱歉这个双重回答,但这里有一种方法可以实现我想要的。感谢 Kevin 指出 pathlib 的来源以及我们这里有构造函数的事实。

import pathlib
import os

def _extramethod(cls, n):
    print("=== "*n)

class PathPlus(pathlib.Path):
    def __new__(cls, *args):
        if cls is PathPlus:
            cls = pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath

        setattr(cls, "extramethod", _extramethod)

        return cls._from_parts(args)

test = PathPlus("C:", "Users", "projetmbc", "onefile.ext")

print("File ?", test.is_file())
print("Dir  ?", test.is_dir())
print("New name:", test.with_name("new.name"))
print("Drive ?", test.drive)

test.extramethod(4)

这将打印以下行。

File ? False
Dir  ? False
New name: C:/Users/projetmbc/new.name
Drive ? 
=== === === === 

您可以对具体实现进行子类化,这样就可以了:

class Path(type(pathlib.Path())):

这是我用这个做的:

import pathlib

class Path(type(pathlib.Path())):
    def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None):
        if encoding is None and 'b' not in mode:
            encoding = 'utf-8'
        return super().open(mode, buffering, encoding, errors, newline)

Path('/tmp/a.txt').write_text("я")

这也是工作。

from pathlib import Path

class SystemConfigPath(type(Path())):
    def __new__(cls, **kwargs):
        path = cls._std_etc()
        return super().__new__(cls, path, **kwargs)

    @staticmethod
    def _std_etc():
        return '/etc'

name = SystemConfigPath()
name = name / 'apt'
print(name)

印刷:

/etc/apt

@staticmethod可以换成@classmethod

我也一直在纠结这个问题。

这是我所做的,从 pathlib 模块学习。 在我看来,这是更简洁的方法,但如果 pathlib 模块更改其实现,它可能不会成立。

from pathlib import Path
import os
import pathlib

class PPath(Path):

    _flavour = pathlib._windows_flavour if os.name == 'nt' else pathlib._posix_flavour

    def __new__(cls, *args):
        return super(PPath, cls).__new__(cls, *args)

    def __init__(self, *args):
        super().__init__() #Path.__init__ does not take any arg (all is done in new)
        self._some_instance_ppath_value = self.exists() #Path method

    def some_ppath_method(self, *args):
        pass

test = PPath("dir", "test.txt")

根据您想要扩展 Path(或 PosixPath 或 WindowsPath)的原因,您也许可以简化您的生活。就我而言,我想实现一个 File class,它具有 Path 的所有方法,以及其他一些方法。但是,我实际上并不关心 isinstance(File(), Path).

委托工作得很好:

class File:

    def __init__(self, path):
        self.path = pathlib.Path(path)
        ...

    def __getattr__(self, attr):
        return getattr(self.path, attr)

    def foobar(self):
        ...

现在,如果 file = File('/a/b/c'),我可以在 file 上使用整个 Path 接口,还可以执行 file.foobar()。

结合之前的一些答案你也可以这样写:

class MyPath(pathlib.Path):
    _flavour = type(pathlib.Path())._flavour

为了从 pathlib.Path 继承,您需要指定您代表的 OS 或“风格”。您需要做的就是通过从 pathlib.PosixPathpathlib.WindowsPath.

继承来指定您使用的是 Windows 或 Unix(根据您的回溯似乎是 Unix)
import pathlib

class PPath(pathlib.PosixPath):
    pass

test = PPath("dir", "test.txt")
print(test)

输出:

dir\test.txt

按照 does the exact same thing as directly inheriting from pathlib.PosixPath or pathlib.WindowsPath since instantiating pathlib.Path "creates either a PosixPath or a WindowsPath" (pathlib documentation 中的建议使用 type(pathlib.Path())

如果您知道您的应用程序不会跨平台,直接从代表您的 OS.

的 flavor Path 继承会更简单