I would like to enhance the class pathlib.Path
but the simple example above dose not work.
from pathlib import Path
class PPath(Path):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
test = PPath("dir", "test.txt")
Here is the error message I have.
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'
What I am doing wrong ?
The pathlib is a Python module which provides an object API for working with files and directories. The pathlib is a standard module. Path is the core object to work with files.
The pathlib module replaces many of these filesystem-related os utilities with methods on the Path object. Notice that the pathlib code puts the path first because of method chaining!
The pathlib module was introduced in Python 3.4 (PEP 428) to deal with these challenges. It gathers the necessary functionality in one place and makes it available through methods and properties on an easy-to-use Path object.
In this article, I have introduced another Python built-in library, the Pathlib. It is considered to be more advanced, convenient and provides more stunning features than the OS library. Of course, we still need to know how to use the OS library as it is one of the most powerful and basic libraries in Python.
You can subclass the concrete implementation, so this works:
class Path(type(pathlib.Path())):
Here's what I did with this:
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("я")
Here is the definition of the Path
class. It does something rather clever. Rather than directly returning an instance of Path
from its __new__()
, it returns an instance of a subclass, but only if it's been invoked directly as Path()
(and not as a subclass).
Otherwise, it expects to have been invoked via either WindowsPath()
or PosixPath()
, which both provide a _flavour
class attribute via multiple inheritance. You must also provide this attribute when subclassing. You'll probably need to instantiate and/or subclass the _Flavour
class to do this. This is not a supported part of the API, so your code might break in a future version of 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.
You may be able to simplify your life depending on why you want to extend Path (or PosixPath, or WindowsPath). In my case, I wanted to implement a File class that had all the methods of Path, and a few others. However, I didn't actually care if isinstance(File(), Path).
Delegation works beautifully:
class File:
def __init__(self, path):
self.path = pathlib.Path(path)
...
def __getattr__(self, attr):
return getattr(self.path, attr)
def foobar(self):
...
Now, if file = File('/a/b/c'), I can use the entire Path interface on file, and also do file.foobar().
I have been struggling with this too.
Here is what i did, studying from the pathlib module. Seems to me that is the cleaner way to do it, but if the pathlib module changes its implementation, it probably won't hold.
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")
Combining some of the previous answers you could also just write:
class MyPath(pathlib.Path):
_flavour = type(pathlib.Path())._flavour
I have opened a bug track here after a little discussion on the Python dev. list.
Sorry for this double answer but here is a way to achieve what I want. Thanks to Kevin that points me to the source of pathlib
and the fact we have here constructors.
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)
This prints the following lines.
File ? False
Dir ? False
New name: C:/Users/projetmbc/new.name
Drive ?
=== === === ===
In order to inherit from pathlib.Path
, you need to specify which OS, or "flavour" you're representing. All you need to do is specify that you are using either Windows or Unix (seems to be Unix based on your traceback) by inheriting from pathlib.PosixPath
or pathlib.WindowsPath
.
import pathlib
class PPath(pathlib.PosixPath):
pass
test = PPath("dir", "test.txt")
print(test)
Which outputs:
dir\test.txt
Using type(pathlib.Path())
as proposed in this answer 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).
If you know your application will not be cross-platform, it is simpler to directly inherit from the flavor Path that represents your OS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With