2

Suppose I have a file known as xyz in a folder as shown below:

Directory
   |
   |+ Subdirectory_n
           |
           |+ Subdirectory_(n-1)
                   |
                   |+ Subdirectory_(n-2)
                           |
                           |+ Subdirectory_(n-3)
                                    ........
                                           |+ Subdirectory_1
                                                   |
                                                   |+ xyz

And if I want the nth subdirectory from the file, how do I obtain it python? For e.g. for the 2nd sub directory I can do something like:

import os
file = open('xyz.dat', 'w+')
print os.path.dirname(os.path.dirname(__file__))

for the third

import os
file = open('xyz.dat', 'w+')
print os.path.dirname(os.path.dirname(os.path.dirname(__file__)))

This method looks a bit awkward. Is there a better way to do this, so that I can generalize it for an nth subdirectory in Python?

Tom Kurushingal
  • 6,086
  • 20
  • 54
  • 86

5 Answers5

3

A recursive approach:

def nth_parent(path, n): return path if n <= 0 else os.path.dirname(nth_parent(path, n-1))

n is the number of levels to go up the directory tree.

Sam
  • 8,330
  • 2
  • 26
  • 51
1

You can get the absolute path of the file using os.path.abspath() That should give you something like 'C:\folder1\folder2\folder3\...'. You can then use string operations to find the nth directory.

MrocKK
  • 51
  • 5
1

You know this calls for a clever-clogs recursive solution but perhaps something like

n=5
while(n):
    path=os.path.dirname(path)
    n -=1

would be clear and preserve the os.path localization magic you would lose by just splitting os.path.abspath().

You could always try something like

os.path.dirname(os.path.normpath(__file__+"../"*n))

but that's not guaranteed to work for all symlinks, etc.

Jeremy Kemball
  • 1,175
  • 1
  • 9
  • 14
1

This is a bit awkward, but short enough to use directly:

reduce(lambda x, y: y(x), [os.path.dirname]*n, path)

rezca
  • 116
  • 6
0

Python ≥ 3.4 implement PEP 428 and brings you pathlib, giving you more flexibility.

The "modern" way to do it could be resumed with:

pathlib.Path('xyz.dat').resolve().parents[nth_parent - 1]

# equivalent of:
filename = os.path.realpath('xyz.dat')
for _ in range(nth_parent):
    filename = os.path.dirname(filename)

Where nth_parent is the number of ancestors and zero mean the immediate parent (hence the - 1 in the example).

Equivalences (with os.path):

filename = 'xyz.dat'
pathlib.Path(filename).resolve(), os.path.realpath(filename)

# PurePath does not have resolve() nor other features added by the Path subclass;
# but when the paths are absolutes, those two are similar
pathlib.Path(__file__) == pathlib.PurePath(__file__)

pathlib.Path(__file__).parents[0], pathlib.Path(__file__).parent, os.path.dirname(__file__)

pathlib.Path(__file__).parents[1], pathlib.Path(__file__).parent.parent, os.path.dirname(os.path.dirname(__file__))
bufh
  • 3,153
  • 31
  • 36