Reputation: 9403
My specific use case is I'm trying to subclass pathlib.Path
. I want to be able to add or override some functionality, but I also want to inherit all of Path. Path has a __new__
and in it has:
if cls is Path:
cls = WindowsPath if os.name == 'nt' else PosixPath
In other words, Path requires passing the proper class to it. The problem is I don't know how to both create MY class and call Path.__new__
with cls == Path
.
I've tried many things and each one gives me a different problem.
This one gives me AttributeError: type object 'RPath' has no attribute '_flavour'
because I'm trying to override the parent class.
Python3:
class RPath(Path):
def __new__(cls, basedir, *args, **kwargs):
return Path.__new__(cls, *args, **kwargs)
def __init__(self, basedir, *pathsegs):
super().__init__()
self.basedir = basedir
def newfunction(self):
print('Something new')
And this one gives returns a Path object, and hence doesn't allow me to do my overrides.
def __new__(cls, basedir, *args, **kwargs):
return Path.__new__(Path, *args, **kwargs)
I also tried various usages of super()
, to no avail.
This seems like it should be pretty easy. What am I missing?
Update: What am I trying to accomplish?
Specifically, I want to make class RPath(basedir, *pathsegments)
:
rpath=RPath('\root\dir', 'relpath\path2\file.ext)
assert rpath.basedir == '\root\dir' # True
rpath.rebase('\new_basedir')
assert rpath.basedir === '\newbasedir' # True
# And while I'm at it
assert rpath.str == str(rpath) # make str a property == __str__(self)
Upvotes: 6
Views: 540
Reputation: 16056
Remember that you are under absolutely no obligation to actually call your superclass's __new__
or __init__
. The only constraint is that you ultimately call object.__new__(cls)
in order to allocate an object of type cls
.
So, simply copy the relevant logic out of your superclass's __new__
into your own.
Upvotes: 0
Reputation: 9403
Here is a solution, though it doesn't use inheritance. I still suspect that there is a straight-up, simple inheritance way to do it (though see BrenBarn's response for the other side).
This method uses composition. The key is using __getattr__
and getattr()
to automatically delegate all requests not found in the wrapper class to the wrapped class. Here is an example:
class RPath(object):
def __init__(self, basedir, *pathsegs):
self.p = Path(*pathsegs)
self.basedir = basedir
# Override existing behavior
def __eq__(self, other):
return type(other) is RPath and self.basedir == other.basedir and self.p == other.p
# Add new behavior
def cp(self):
return self.basedir / self.p
# Everything not found here, automatically delegate to Path
def __getattr__(self, attr):
return getattr(self.cp, attr)
Upvotes: 1
Reputation: 251373
I don't think that's going to be possible in the usual way. But even if you could do it, it wouldn't work, because what Path is doing is not returning a plain Path either, it's returning some subclass (WindowsPath or PosixPath). So your overrides to Path won't take effect, because if you were able to inherit Path.__new__
, it would still return, say, a WindowsPath, and WindowsPath inherits from Path, not your custom path subclass.
It seems pathlib.Path
has an idiosyncratic class structure, and you'll have to do some special work to duplicate it. At a first guess, you would need to make your own subclasses of WindowsPath and PosixPath, and then make a Path subclass that delegates to instantiate one of those instead of itself.
Upvotes: 2