子类“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'
我做错了什么?
Here是Path
class的定义。它做了一些相当聪明的事情。它不是直接从其 __new__()
返回 Path
的实例,而是 returns 子 class 的实例,但 仅 如果它被直接调用为 Path()
(而不是作为子class)。
否则,它应该通过 WindowsPath()
或 PosixPath()
调用,它们都通过多重继承提供 _flavour
class 属性。您还必须在 subclassing 时提供此属性。您可能需要实例化 and/or subclass the _Flavour
class 来执行此操作。这不是 API 的受支持部分,因此您的代码可能会在 Python.
的未来版本中中断
根据凯文的观察,这里有一个简单的方法。
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.PosixPath
或 pathlib.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 继承会更简单
我想增强 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'
我做错了什么?
Here是Path
class的定义。它做了一些相当聪明的事情。它不是直接从其 __new__()
返回 Path
的实例,而是 returns 子 class 的实例,但 仅 如果它被直接调用为 Path()
(而不是作为子class)。
否则,它应该通过 WindowsPath()
或 PosixPath()
调用,它们都通过多重继承提供 _flavour
class 属性。您还必须在 subclassing 时提供此属性。您可能需要实例化 and/or subclass the _Flavour
class 来执行此操作。这不是 API 的受支持部分,因此您的代码可能会在 Python.
根据凯文的观察,这里有一个简单的方法。
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.PosixPath
或 pathlib.WindowsPath
.
import pathlib
class PPath(pathlib.PosixPath):
pass
test = PPath("dir", "test.txt")
print(test)
输出:
dir\test.txt
按照 pathlib.PosixPath
or pathlib.WindowsPath
since instantiating pathlib.Path
"creates either a PosixPath or a WindowsPath" (pathlib documentation 中的建议使用 type(pathlib.Path())
。
如果您知道您的应用程序不会跨平台,直接从代表您的 OS.
的 flavor Path 继承会更简单