Подкласс `pathlib.Path` не работает

Я хотел бы улучшить класс 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'

Что я делаю неправильно?

Ответ 1

Вы можете подклассифицировать конкретную реализацию, поэтому это работает:

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("я")

Ответ 2

Здесь - это определение класса Path. Он делает что-то довольно умное. Вместо прямого возврата экземпляра Path из его __new__() он возвращает экземпляр подкласса, но только если он был вызван непосредственно как Path() (а не как подкласс).

В противном случае ожидается, что он будет вызван через WindowsPath() или PosixPath(), которые оба предоставляют атрибут класса _flavour через множественное наследование. Вы также должны указать этот атрибут при подклассовке. Для этого вам, вероятно, потребуется создать экземпляр и/или подклассы класса _flavour. Это не поддерживаемая часть API, поэтому ваш код может сломаться в будущей версии Python.

TL; DR: Эта идея чревата опасностью, и я боюсь, что мои ответы на ваши вопросы будут интерпретироваться как утверждение вместо неохотной помощи.

Ответ 3

Примечание

Я открыл здесь трек ошибок после небольшого обсуждения на Python dev. список.

Временное решение

Извините за этот двойной ответ, но вот способ добиться того, чего я хочу. Благодаря Кевину, который указывает мне на источник 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 ? 
=== === === === 

Ответ 4

Вот простой способ сделать что-то относительно наблюдения Кевина.

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

Затем мне нужно будет использовать трюк, чтобы автоматически привязывать все методы Path к моему классу PPpath. Я думаю, что это будет забавно делать.