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

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.

Upvotes: 2

Views: 669

Answers (3)

After watching a video https://youtu.be/U2OcGe-qSEY, I thought of coming back to this question. Well, you don't need much. If you are not going to use the subpackages 1 and 2 independently later, then you even don't need the __init__.py file in those two folders. You also don't need the main.py file in my old answer. You even don't need to write anything inside the __init__.py file of the main package for the purpose of calling functions of the files in this project inside different parts. You can also avoid the import sys; sys.path.append('./') in module2.py. What you need is just a relative path with respect to the main package. So at the top of module2.py you can write from ..subpackage1.module1 import fun1. From left to right, we first see a double dot, the first dot in the right says look at the folder containing this file, which is subpackage2, then the second dot behind it says go one folder up, so now we are in main_package, then it says go to subpackage1, and then module1 and then pick up fun1 with the same name so in this file you can simply call fun1.

Now if you import main_package somewhere else, you have access to everything. Of course you have to call the functions with their intermediate subpackages. If you want to call them as immediate belongings of main_package, then you can put those import lines of my old answer in the __init__.py file of main package so you can call them as main_package.fun1 and not from main_package.subpackage1 import fun1.


Old answer:

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.

Upvotes: 1

hyit
hyit

Reputation: 721

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.

Upvotes: 2

Valentin Grégoire
Valentin Grégoire

Reputation: 1150

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.

Upvotes: 2

Related Questions