I am trying to import all the objects from a subfolder into a class in python 3.8, and am strugguling to find a way to do so. I don't want to manually have to import all the objects, as there are far too many files to list:
class Foo:
from bar import {
one,
two,
three,
# ...
}
And when I use the star symbol to import all functions and classes (e.g. from bar import *
) I get the following error:
SyntaxError: import * only allowed at module level
Also, I would not like to put everything under a sub-scope (e.g. import bar.package
and put all the files in the package
subpackage of bar
), because the functions in bar
rely on being passed self
when run and would mean would have to change all references of self
to self.package
I am only creating another folder with all the methods in the class so that there is not a single extremely long file.
So I guess I have three questions: Why is importing all in a class not allowed, how can I get around this, and is there a better way to split multiple methods up into different files?
EDIT: I've seen this post, and is what I currently have, but only instead of importing everything manually I want to use the *
instead.
EDIT 2: Perhaps creating the submethods of a class as a package and importing it as self would work (e.g. import bar.package as self
), but this may override the default self. For example say I have the two files foo.py
and bar.py
:
# foo.py
def print_bar(self):
print("bar")
# bar.py
class Test:
import foo as self
def __init__(self):
print("hi")
self.one = 1
def print_one(self):
print(self.one)
if __name__ == "__main__":
test_class = Test()
test_class.print_one()
test_class.self.print_bar(test_class)
Notice the ugly call in bar.py
that runs test_class.self.print_bar(test_class)
. In this case, python does not automatically pass the class as a method. If I get rid of the test_class
argument passed in the function it does not run and gives a TypeError
. I want to avoid passing self
to the method at all costs.
First of all: Don't do this.
There are probably better ways to solve your specific problem. If you have to define a large number of related methods for a class, and want to use modules to organize these methods, then give each module a base class, define the methods on those classes, then combine the classes from the modules into one by using sub-classing. See below.
Why is importing all in a class not allowed
Because a class statement is a scoped namespace where Python, at compile time, needs to know what names are referenced as globals and distinguish those from local names. This is especially important in functions, where the local namespace is highly optimised by knowing what names are used at compile time. For a class statement, the 'local' names become the class attributes. A class statement is more flexible when it comes to dynamic local names than functions are, but importing an arbitrary, dynamic list of names into a class definition was never seen as a use-case worth supporting.
how can I get around this
You can introspect the module and get all the same names that Python would import, and set these on the class dynamically using setattr()
or by assigning to the locals()
dictionary (a class statement is the only place the latter actually works). The import
statement documentation lists what is imported:
__all__
, then it is used as the list of names to import_
).So the following would achieve the exact same effect as using from bar import *
inside the class statement for Foo
:
import bar
def _import_all(module, class):
namespace = vars(module)
public = (name for name in namespace if name[:1] != "_")
for name in getattr(module, "__all__", public):
setattr(class, name, namespace[name])
class Foo:
pass
_import_all(bar, Foo)
or, and this is definitely more 'hack-y' and depending on internal Python implementation details:
import bar
class Foo
locals().update(
(n, getattr(bar, n))
for n in getattr(
bar, "__all__",
(n for n in dir(bar) if n[:1] != "_")
)
)
is there a better way to split multiple methods up into different files?
Yes, use classes. Put all those methods in separate modules, each in a class, then simply import those classes from each module and use it as a base class:
import bar, spam
class Foo(bar.Base, spam.Base):
pass
where bar.py
would define a Base
class:
class Base:
def print_bar(self):
print("bar")
and so would spam
, etc. You can add and remove methods to these base mixin classes as needed, and the import
statements to combine the base classes will not change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With