8
\$\begingroup\$

I use the following function for opening files in Python:

def file_path(relative_path):
    folder = os.path.dirname(os.path.abspath(__file__))
    path_parts = relative_path.split("/")
    new_path = os.path.join(folder, *path_parts)
    return new_path

I call it with a relative path, like this:

with open(file_path("my_files/zen_of_python.txt")) as f:
    zop = f.read()

I like it because you can use a relative path works no matter where the Python script is executed from:

  1. folder = os.path.dirname(os.path.abspath(__file__)) gets the absolute path to the folder holding the python script.
  2. os.path.join(folder, *path_parts) gets an os-independent path to the file to open.

To further explain, let's say I have the following folder structure:

- parent_folder
  - example.py
  - my_files
    - zen_of_python.txt

If example.py looks like this:

with open("my_files/zen_of_python.txt") as f:
    zop = f.read()

Then I have to run example.py from the parent_folder directory or else it won't find my_files/zen_of_python.txt.

But if I open my_files/zen_of_python.txt using the file_path() function shown above, I can run example.py from anywhere.

One downside as @infinitezero pointed out is that you can't use an absolute path, but for my purposes, that's okay for my purposes. The script is self-contained. I'm not passing in external files when I run it.

Can anyone see any downside to this? Or does anyone have a more Pythonic way of accomplishing the same thing?

\$\endgroup\$
0
3
\$\begingroup\$

Your code represents a well-known pattern. In Java, files like this are called resource files and they are delivered together with the code in a .jar file (which is essentially a .zip file).

As pointed out in a comment, you cannot use your code with absolute paths. This is good since the entire purpose of the code is to find a resource relative to the source code that needs this resource.

The call to abspath looks redundant to me. I'm assuming that __file__ is already an absolute path. You may want to check the documentation about it.

A downside of your function is that you have to define it in each file that wants to open relative files since it uses __file__. You cannot import that function, as it is now.

\$\endgroup\$
1
  • \$\begingroup\$ If you execute a script using a relative path (e.g., python my_file.py), then __file__ will be relative. You're right that a downside is that you cannot import the function unless it's in the same directory. You could fix this by accepting __file__ as a 2nd param. \$\endgroup\$
    – Webucator
    Feb 5 '20 at 16:14
1
\$\begingroup\$

If using just os.path, this looks almost perfect.

  • Move folder into the global scope, as there's no point not to. It means that the function can be made simpler - as it's now split into two seperate entities.
FOLDER = os.path.dirname(os.path.abspath(__file__))


def file_path(relative_path):
    return os.path.join(folder, *relative_path.split("/"))

It would be better if you used pathlib. This is the modern version of os.path. If you do, then I would recomend just making folder as there would be no need for the function.

FOLDER = pathlib.Path(__file__).resolve().parent

with (FOLDER / 'my_files/zen_of_python.txt').open() as f:
    ...

Here are some examples of running it on Windows in Python 3.8.

>>> import pathlib
>>> FOLDER = pathlib.Path('foo')
>>> FOLDER / 'bar/baz'  # Unix style
WindowsPath('foo/bar/baz')
>>> FOLDER / 'bar\\baz'  # Windows style
WindowsPath('foo/bar/baz')
>>> FOLDER / 'bar' / 'baz'  # Pathlib style
WindowsPath('foo/bar/baz')
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.