Misunderstanding of python os.path.abspath

Question:

I have following code:

directory = r'D:images'
for file in os.listdir(directory):
    print(os.path.abspath(file))

and I want next output:

  • D:imagesimg1.jpg
  • D:imagesimg2.jpg and so on

But I get different result:

  • D:codeimg1.jpg
  • D:codeimg2.jpg

where D:code is my current working directory and this result is the same as

os.path.normpath(os.path.join(os.getcwd(), file))

So, the question is: What is the purpose of os.path.abspath while I must use

os.path.normpath(os.path.join(directory, file))

to get REAL absolute path of my file? Show real use-cases if possible.

Asked By: Most Wanted

||

Answers:

listdir produces the file names in a directory, with no reference to the name of the directory itself. Without any other information, abspath can only form an absolute path from the only directory it can know about: the current working directory. You can always change the working directory before your loop:

os.chdir(directory)
for f in os.listdir('.'):
    print(os.path.abspath(f))
Answered By: chepner

The problem is with your understanding of os.listdir() not os.path.abspath(). os.listdir() returns the names of each of the files in the directory. This will give you:

img1.jpg
img2.jpg
...

When you pass these to os.path.abspath(), they are seen as relative paths. This means it is relative to the directory from where you are executing your code. This is why you get “D:codeimg1.jpg”.

Instead, what you want to do is join the file names with the directory path you are listing.

os.path.abspath(os.path.join(directory, file))
Answered By: unholysampler

Python’s native os.listdir and os.path functions are pretty low-level. Iterating through a directory (or a series of descending directories) requires your program to assemble file paths manually. It can be convenient to define a utility function that generates the paths you’re going to need just once, so that path assembly logic doesn’t have to be repeated in every directory iteration. For example:

import os

def better_listdir(dirpath):
    """
    Generator yielding (filename, filepath) tuples for every
    file in the given directory path.
    """
    # First clean up dirpath to absolutize relative paths and
    # symbolic path names (e.g. `.`, `..`, and `~`)
    dirpath = os.path.abspath(os.path.expanduser(dirpath))

    # List out (filename, filepath) tuples
    for filename in os.listdir(dirpath):
        yield (filename, os.path.join(dirpath, filename))

if __name__ == '__main__':
    for fname, fpath in better_listdir('~'):
        print fname, '->', fpath

Alternatively, there are “higher level” path modules that one can employ, such as py.path, path.py, and pathlib (now a standard part of Python, for version 3.4 and above, but available for 2.7 forward). Those add dependencies to your project, but up-level many aspects of file, filename, and filepath handling.

Answered By: Jonathan Eunice
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.