__init__.py import default classes - python

Say I have ClassA, ClassB and ClassC each within their own .py files called ClassA.py, ClassB.py and ClassC.py respectively.
# Inside ClassA.py
class ClassA:
pass
In my __init__.py I end up with
from .ClassA import ClassA
from .ClassB import ClassB
from .ClassC import ClassC
Every time I add a new class (using the same structure), I have to change the __init__.py. Is there a recommended way to automate this, given that all files in this package follow the same (not-quite-pythonic-but-oh-well) structure?

It is possible, but not recommended.
Consider the following directory structure:
├── test_module
│   ├── ClassA.py
│   ├── ClassB.py
│   └── __init__.py
We want to import ClassA.ClassA and ClassB.ClassB into __init__.py programmatically.
Assume these are the contents of ClassA.py:
class ClassA:
pass
ClassB.py is identical, save for the name of the class.
Now, say that in __init__.py we want to traverse the root directory non-recursively (not that it matters, since we have no directories) and import all classes from all modules within.
__init__.py:
import os
from importlib import import_module
my_location = os.path.dirname(__file__)
module_list = [file
for file in os.listdir(my_location)
if os.path.splitext(file)[1] == '.py'
and file != '__init__.py']
modules = [import_module(f'.{os.path.splitext(module)[0]}', __name__)
for module in module_list]
After running import test_module, the classes from ClassA.py and ClassB.py will be imported into the working namespace as test_module.ClassA.ClassA and test_module.ClassB.ClassB respectively.
To illustrate:
>>> import test_module
>>> test_module.ClassA.ClassA()
<test_module.ClassA.ClassA object at 0x7f1e66181fd0>
For completeness, if you want this script to mimic the behaviour of from X import Y:
globals().update({name: getattr(module, name)
for module in modules
for name in module.__dict__
if not name.startswith('_')})
Importing:
>>> import test_module
>>> test_module.ClassA()
<test_module.ClassA.ClassA object at 0x7fb8edb9dfd0>
These will make those names accessible as test_module.ClassA etc. (because you are importing from test_module, we add an additional layer of indirection. In test_module's scope, they are accessible directly as unqualified name.
There are additional bells and whistles we could add, such as checking each module's __all__ attribute and performing recursive traversal of sub-directories, but that is rather out of scope of this question, and I must emphasise that to my mind, it would be better to refactor your code such that this is not needed in the first place, rather than tack on functionality that plays with the internals of Python where it does not seem to be essential.

Yes, you can. This is what the importlib package is for. It contains the lower level calls that actually do the importing. It lets you do imports by calling the methods it provides.
So you could use Python os.path.listdir or whatever to learn what your subpackages are, and then make calls to importlib to import each of them.
I haven't done much of this myself. Just enough to know this exists. Maybe someone else can give you more details. Also, this link might be helpful to you:
https://dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805

Related

How to dynamically create classes in __init__.py

Is it possible to dynamically create classes in the __init__.py file.
I'll give an example. Let's say I have this hierarchy:
script.py
module\
--> __init__.py
In the python file script.py, I want to know if I can, with this call:
from module import my_class
dynamically create the my_class definition in the __init__.py

Why this import related code works in __init__.py but not in different .py file?

Let's have this __init__.py in a Python3 package:
from .mod1 import *
from .mod2 import *
from .mod3 import *
__all__ = mod1.__all__ + mod2.__all__ + mod3.__all__
The code looks quite simple and does what is expected: it imports from modules mod1, mod2 and mod3 all symbols that these modules have put into their __all__ list and then a summary of all three __all__ lists is created.
I tried to run the very same code in a module, i.e. not in the __init__.py. It imported the three modules, but mod1, mod2 and mod3 were undefined variables.
(BTW, if you run pylint on the original __init__.py, you will get this error too.)
The same statement from .mod1 import * creates a mod1 object when executed in the __init__.py, but does not create it elsewhere. Why?
__init__.py is a special file, but till now, I thought only its name was special.
According to the documentation, this is expected behaviour:
When a submodule is loaded using any mechanism (e.g. importlib APIs, the import or import-from statements, or built-in __import__()) a binding is placed in the parent module’s namespace to the submodule object. For example, if package spam has a submodule foo, after importing spam.foo, spam will have an attribute foo which is bound to the submodule.
In other words, when you do a from .whatever import something within a module, you will magically get a whatever attribute bound to the module. Naturally, you can access module's own attributes within __init__.py as if they were defined as variables there. When you are in another module you cannot do it. In this sense __init__.py is special indeed.

How to create a python package with multiple files without subpackages

I am attempting to create a package (mypackage) that contains a few classes, but would like the classes contained in multiple files.
For example, I want class_a.py to contain a class named ClassA, etc...
Thus, I would like the following file structure:
.../mypackage
__init__.py
class_a.py
class_b.py
...
However, I would like to load and use the package as follows:
load mypackage
a = mypackage.ClassA()
What do I need to do (I assume in the __init__.py) file to make this possible. Currently, it operates using "mypackage.class_a.ClassA()"?
As mentioned, in your __init__.py for a class, use the following:
from class_a import ClassA
from class_b import ClassB
for the case of a file without a class, use the following:
from . import file_a
from . import file_b
or if you only want to expose specific methods of a file:
from .file_a import method_a
from .file_b import method_b
Make your __init__.py import all your ClassA, ClassB, etc from other files.
Then you'll be able to import mypackage and use mypackage.ClassA, or from mypackage import ClassA and use it as unqualified ClassA.
A bit of background.
An import foo statement looks for foo.py, then for foo/__init__.py, and loads the names defined in that file into the current namespace. Put whatever you need to be "top-level" into __init__.py.
Also, take a look at __all__ top-level variable if you tend to from mypackage import *.
In your __init__.py, add this:
from class_a import ClassA
from class_b import ClassB
del class_a
del class_b

Importing specific classes from a file located in a python package

I have a python package main and other_python_files which are like:
main/
__init__.py
lib.py
other_python_files/
__init__.py
test.py
Let lib.py contain a class called MyClass. When I do from main import lib.py and use MyClass inside test.py I get the error that MyClass is not defined.
I tried doing from main import MyClass inside the init file in the main directory but I still get the same error. What should I do to achieve importing a specific class from the lib.py file ?
You either have to import that class out of lib:
from main.lib import MyClass
Or use lib.MyClass in place of MyClass.
You can also import MyClass inside of the __init__.py file that's in main, which lets you import it the way you originally tried:
__all__ = ['MyClass']
from lib import MyClass
You can read about __all__ here: Can someone explain __all__ in Python?

what does importing a module in python mean?

I am new to python and found that I can import a module without importing any of the classes inside it. I have the following structure --
myLib/
__init__.py
A.py
B.py
driver.py
Inside driver.py I do the following --
import myLib
tmp = myLib.A()
I get the following error trying to run it.
AttributeError: 'module' object has no attribute A
Eclipse does not complain when I do this, in fact the autocomplete shows A when I type myLib.A.
What does not it mean when I import a module and not any of the classes inside it?
Thanks
P
Python is not Java. A and B are not classes. They are modules. You need to import them separately. (And myLib is not a module but a package.)
The modules A and B might themselves contain classes, which might or might not be called A and B. You can have as many classes in a module as you like - or even none at all, as it is quite possible to write a large Python program with no classes.
To answer your question though, importing myLib simply places the name myLib inside your current namespace. Anything in __init__.py will be executed: if that file itself defines or imports any names, they will be available as attributes of myLib.
If you do from myLib import A, you have now imported the module A into the current namespace. But again, any of its classes still have to be referenced via the A name: so if you do have a class A there, you would instantiate it via A.A().
A third option is to do from myLib.A import A, which does import the class A into your current namespace. In this case, you can just call A() to instantiate the class.
You need to do
from mylib import A
Because A is not an attribute of __init__.py inside mylib
When you do import mylib it imports __init__.py
See my answer.
About packages

Categories