Importing file from another imported file

549 Views Asked by At

I have the following directory structure:

base_folder
    methods_folder
        method_1.py
        method_2.py
        .
        .
        .
        method_n.py
        class_methods.py
    top_class.py
    

class_methods.py imports the other files in the same directory, like this:

from method_1 import method_1
from method_2 import method_2
.
.
.
from method_n import method_n

(obs: these methods files has a method with its own file names inside them)

If I run class_methods.py by myself, no problem. But if I try to run top_class.py, which imports class_methods.py, I get the error no module named method_1

So, when executing top_class.py, it is not seeing the files in methods_folder/. Why?

5

There are 5 best solutions below

0
On

the correct import inside top_class.py would be from methods_folder.method_n import method_n. This is because you are treating methods_folder as a package. If you are running a version of Python that is before 3.3 you must also unclude __init__.py file inside the methods_folder in order to turn it into a package.

2
On

I assume you are running them from their respective directories? Unless they are installed in your Python path (I'm going to assume they are not), then Python will by default look for imports in your current directory. So when you run class_methods.py from its directory, then it can find methods_1.py in order to satisfy from methods_1 import method_1. But when you execute top_class.py, it looks for methods_1.py or methods_1/__init__.py, neither of which it fines from that directory.

Assuming Python 3, you would need to use relative imports in class_methods.py.

# class_methods.py
from .methods_1 import method_1
from .methods_2 import method_2

This will let you run it from top_class.py. Unfortunately, you can't use relative imports when running a script in the same package, so you wouldn't be able to run class_methods.py directly.

Another option in that case is to keep the absolute imports in class_methods.py and add the methods folder to the path in top_class.py.

import os 
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), 'methods'))

import class_methods
from methods_1 import method

Of course editing sys.path is fine for small scripts and standalone things. But if this grows past being a script, you'll need to be careful about doing it, and you'll probably just want to come up with another solution. Likely, the best thing is to create a package that you install (you can still do this from your source directory while you are developing) and then you can import the same way from anywhere.

0
On

Files only have direct access to things they import. Say we have a.py which imports b.py, and b.py imports c.py. When running within a.py functions in b.py that use c.py, this will work fine, because a has access to b, and b has access to c. This does not mean, however, that the imports chain (as in C++) and you can use functions from c in a. You will get an error, because a can only see the contents of b, which it imported.

So if you want to use all your method_i.py files from within top_class.py, you need to import them directly in the same file.

Edit: You also have some other issues. To import other files in a subfolder, you would need to, inside top_class.py, call import methods_folder.method_i. To import something in the same directory, just use import method_i. Since you have a method of the same name in each file, what you have works fine in class_methods.py You also need to create an empty file called __init__.py in any folder containing python files you intend to import to/from which lets python know it's allowed to look there.

0
On

You can create an importable package in python in one of two ways. The first way is what you are doing: you create a file called my_package.py and import it with import my_package. This is commonly used for simpler packages that don't need to be further broken up. For this to work, your .py file has to be on the PYTHONPATH which is an environment variable that tells python where to look for packages. If not defined, there are some default places that python will use to look for packages. One of these default locations is the current working directory, which is why your first set of imports works.

In theory you should be able to run the second piece of code from the same location (python ../top_class.py) and use the same import style, but I assume you are changing directories to run that file. This means your files are no longer in the current working directory and no longer findable by python.

One way to get your code to work using the existing style would be to define PYTHONPATH with the location of your methodX.py files. You will typically add to the python search path like this:

PYTHONPATH=$PYTHONPATH:./methods_folder python top_class.py

This tells python to look in methods_folder, in addition to the standard places, when you try to import something. Playing with the PYTHONPATH gets a little annoying after a while, so I actually prefer the next approach.

A second way to create a package is by creating a folder with an __init__.py file inside. This tells python that you want it to treat that directory as a package. This is the preferred style for more complicated pieces of code that might benefit from organization across multiple files. For your example, you could organize your code in the following way:

base_folder
    methods_folder
        __init__.py
        method_1.py
        method_2.py
        .
        .
        .
        method_n.py
        class_methods.py
    top_class.py

And then your import in top_class.py would look like this:

from methods_folder.method1 import method1
from methods_folder.method2 import method2
from methods_folder.method3 import method3

This has the effect of creating a top level methods_folder package with modules method1, method2, etc. Because methods_folder is in the same directory as the one you are running top_class.py from, python picks that up as a package using the default PYTHONPATH and lets you import from within it.

0
On

The recommended way of running a python script is using the -m switch from the parent of your root package - so in your case:

$ cd base_folder
$ python -m top_class

Python will automatically add the base_folder to its sys.path and you don't need to do any sys.path/PYTHOPATH hacks that are just this - hacks that bloat the code with boilerplate and will blow when least expected.

Now to run the class_methods the correct way is also

$ cd base_folder
$ python -m methods_folder.class_methods

but then the imports in class_methods should be modified to either absolute:

from methods_folder.method1 import method1
...

or relative:

from .method1 import method1
...