How to import a function in one module to a module in a different folder?

Viewed 136

Imagine a package with this structure:

└── main_package
    ├── __init__.py
    ├── subpackage1
    │   ├── __init__.py
    │   └── module1.py
    └── subpackage2
        ├── __init__.py
        └── module2.py

What is the best way of importing a function in module1 to module2?

Should the __init__.py files contain something?

The best solution I have found is to include in module2

import sys
sys.path.append('./')
from main_package.subpackage1.module1 import fun1

And when running module2 from the directory in which main_package is it works.

But I feel like this does not take advantage of the fact that they are packages. And also it would be nice to be able to run this from wherever I want if possible.

3 Answers

The most reliable is to always use absolute imports. In this case for module 1:

from MainPackage.module2 import some_function

In module1_1 that would be:

from MainPackage.Subpackage2.module2_1 import some_function

Note that the package naming convention is to have all lower case letters and in between words, an underscore (_) can be used. In your specific example, MainPackage would become main_package and so on.

For your first question:

What is the best way of importing a function in module1 to module2?

You may either use relative or absolute imports. Personally, I prefer to use relative imports only within the context of a package I'm working on - to me this highlights when internal packages are related. In addition (and this is environment specific), certain environments will not recognize main_package until you install the package (i.e. with pip install -e . during development).

Absolute import in module2: from main_package.subpackage1.module1 import my_function
Relative import in module2: from ..subpackage1.module1 import my_function

For your second question:

Should the init.py files contain something?

They can, and that may provide you with shortcuts for imports. It is common practice to expose general and/or first-class functions/classes on the package level, which essentially is just importing them in __init__.py.
For example, in subpackage1\__init__.py you could:

from .module1 import my_function

And then in subpackage2\module2.py, you could simply use from ..subpackage1 import my_function (relative import) or from main_package.subpackage1 import my_function.

You may also be interested in the topic from the original Python documentation - import/submodules, Importing * From a Package (for additional reading on the import system, followed by intra-package imports), PEP328 (the rationale for relative imports) and Guido's thoughts regardings relative imports.

In Maple importing a module in the main file of your module will automatically share it with the other files and submodules of your module. You can also call the functions that you defined in different files of the same module as far as you have linked them by just including the address to the files of the module in the main file. So you only type the addresses in one file and you do not need to write them in other files of your module. In my opinion that makes life very easier.

In Python I couldn't do the same as in Maple and here is what I do in my current practice. Let me explain on a toy example. There is a folder containing a python file main.py and the package structure as you showed in your question. The content of each file is as follows.

main.py

import main_package

main_package.fun2()

The __init__.py of main_package.

from .subpackage1.module1 import fun1
from .subpackage2.module2 import fun2

The two init files of subpackages are empty.

module1.py

import sympy
x = sympy.Symbol('x')
def fun1(polynomial):
    return(sympy.degree(polynomial, gen = x))

module2.py

import sympy
from ..subpackage1.module1 import fun1
def fun2():
    polynomial = sympy.sympify(input("Type a univariate polynomial with the variable x and integer coefficients:"))
    print(f"Degree of your polynomial in x is {fun1(polynomial)}.")

Now, instead of running the __init__.py of main_package, I run main.py in my VSCode. Note that my main.py file is next to main_package folder, not inside of it.

Again, I think it is sad that I have to write import sympy in each of the two files module1.py and module2.py. I am very interested if someone someday shows a way so that one can write import sympy and x = sympy.Symbol('x') only in the top parent __init__.py and avoid writing it in the two files module1.py and module2.py.

Related