So, i was looking at the pathlib source code and i noticed some os lib functions were incorperated into the pathlib classes. I must admit that it took me a little while to understand how this works.
import os
class Foo:
listdir = os.listdir
join = os.path.join
walk = os.walk
def __init__(self, name):
self.name = name
def __fspath__(self):
return self.name
curr = Foo('.')
pipo = Foo('pipo')
This class definition adds the listdir, join and walk functions of the os library as methods on the Foo class. The Foo class is recognized as a PathLike object because it also has the __fspath__
method.
When curr.join('bar')
is called it will join the name of curr, '.', with 'bar'. The code below shows this exact behaviour, with results i would expect.
$ tree
.
├── pipo
│ └── clown
└── test.py
1 directory, 2 files
$
>>> curr.join("baz")
./baz
>>>
>>> pipo.join("baz")
pipo/baz
>>>
>>> [x for x in curr.walk()]
[('.', ['pipo'], ['test.py']), ('./pipo', [], ['clown'])]
>>>
>>> [x for x in pipo.walk()]
[('pipo', [], ['clown'])]
>>>
Then i tried the listdir function ... and it failed. The listdir function shows different behaviour and i'm stumped as to why?
>>>
>>> os.listdir()
['pipo', 'test.py']
>>>
>>> curr.listdir()
['pipo', 'test.py']
>>>
>>> os.listdir("pipo")
['clown']
>>>
>>> pipo.listdir()
['pipo', 'test.py']
>>>
The listdir method on pipo shows the same output as the listdir on curr?
It seems to me like join
and walk
accept the self reference but somehow listdir
does not. Why would that be?
The function
class implements the descriptor protocol, allowing os.path.join
to act as an instance method. os.listdir
is not an instance of function
; it's an instance of builtin_function_or_method
, which does not implement the descriptor protocol.
curr.join(...)
is equivalent to Foo.join.__get__(curr, Foo)(...)
. curr.listdir()
is equivalent to Foo.listdir()
: curr.listdir
does not exist, and Foo.listdir
just calls listdir
, not the non-existent listdir.__get__
which would take care of passing curr
as the implicit first argument.