HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Subclassing pathlib.Path

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pathpathlibsubclassing

Problem

This arises from the SO Question subclassing pathlib.Path.

The pathlib sources show that Path, when invoked, selects one of its subclasses, WindowsPath or PosixPath, to invoke in place of itself, within it's __new__ method. The following diagram shows a rough idea, with black lines indicating inheritance, red the constructor and green the instance returned.

Kevin points out that while this works for Path itself it prevents any subclass from having the same functionality. Now I'm not entirely clear as to why this is and Kevin does not offer an explanation. Presumably the __new__ method is called before the MRO is setup and one can not rely on super to do the right thing just yet, namely bumping up to the super classes __new__ to select the appropriate subclass.

The answers provided for the question itself include the following alternatives and I'm curious as to which is the best method/better practice.

-
Since everyday should be Christmas let's Wrap the class (a.k.a. Encapsulation/Clojures). I find quite often this is actually the go to answer for a lot of inheritance questions which I feel kinda defeats the point of subclassing as one then has to manually expose the wrapped classes methods.

from pathlib import Path as _Path_

class Path():
 def __init__(*args, **kvps):
  super().__init__(*args, **kvps)
  self._path_ = _Path_(*args, **kvps)


-
Instantiate the type of an instantiated class. This is a bit of trickery as you're invoking the class you plan to subclass. It selects the final class for you and you then determine the type it selected and subclass that.

from pathlib import Path

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


-
Tack on the required features to the selected class. This is roughly what projetmbc did.

```
from pathlib import Path as _Path_, PosixPath, WindowsPath

class Path(_Path_):
def __new__(cls, *args):
if cls is Path:
cls = WindowsPath if os.name == 'nt' else PosixPath
setattr(cls, "extrametho

Solution

@Danninno asked us to close off the question, to be honest I forgot about the request for a bit until recently (I've been using the wrapper quite a lot).

Answer

I see that within my own code I went with Option 4. Mostly for the reasons stated in the question. It turned out to be the better fit of the 5 possible options. Option 5 was/is a close contender as it's more OOP'y but it does generate a crazy class structure to simply hide an implementation detail.

Motivation/Discussion

When I originally asked the question I was hoping for bit of a fight/haggle/explanatory answer (Even if I received an opinionated one i.r.o. the forum rules) or atleast a good argument as to which method was the better/worse one. Mostly so that I could improve my decision making process. I see now that some classes in Python are done by it's developers to showcase certain features (After all they like to play/push the bounds somewhat). In this instance Path is constructed in such a way as to avoid the use of Metaclasses (I might be a bit off here, as it's been a while since I looked at the source for pathlib). Another example of this "playing" is seen in the NamedTuple implementation which essentially evals a string to implement it's class, which eliminates any implied inheritance.

Context

StackExchange Code Review Q#162426, answer score: 2

Revisions (0)

No revisions yet.