Optimizing imports in python
I have a folder that contains several files with the extension. py and inside each file has a dictionary with the data of the monsters I need to start. However, the greater the amount of monsters I create, the Greater this list of imports will get. Do you have any way to import the entire folder and all dictionaries at once?
from units import Monster
from data.creatures.monster_list.rat import rat
from data.creatures.monster_list.snake import snake
from data.creatures.monster_list.scorpion import scorpion
from data.creatures.monster_list.lion import lion
from data.creatures.monster_list.wolf import wolf
from data.creatures.monster_list.warlock import warlock
MONSTERS = [rat,snake,scorpion,lion,wolf,warlock]
2 answers
The ideal here is to have a scheme to do a dynamic import of files in the same folder. This code can run in the __init__
file of a folder - and as soon as you import that folder as a package, all the .py
files in it are read.
Traditionally, the way to import a module from a string (i.e., a data, in contrast to the module name typed directly into the program as a parameter to the import
command) is with the call __import__
. Pr issues historical, however, its use is more complicated than it needs to be-with a single parameter it ends up returning the root package of the string you passed (in your example, the package data
)
Then, generically, you can make an import that brings everything you have in the directory. In your case, since you want to make the imported classes available in a data structure (a list), it may be worth doing something more targeted:
from importlib import import_module
from pathlib import Path
def import_creatures(package_name):
root_package = import_module("package_name")
package_path = Path(root_package.__file__).parent
creature_classes = []
for source_file in package_path.glob("*.py"):
module = import_module(source_file)
name = source_file.stem
# Tenta importar a classe se tiver nome comecando com letra maiuscula também
cls = getattr(module, name, getattr(module, name.title(), None))
if not cls:
continue
creature_classes.append(cls)
return creature_classes
MONSTERS = import_creatures("data.creatures.monster_list")
This code always tries to catch the class that has the same name as the file, as in your example - but you can also introspect the module (by taking module.__dict__.items()
for example), and take all the objects that are subclass of a specific class - if all your own inherit from "Monster" for example - then you can safely have more than one definition of Monster per file, and still have freedom to name the classes.
Changing the subject -what about this game? Will he be released? Is it in a public repository? Prune contribute?? :-)
Another note, as the first package of your import name is data
, probably your project is not yet structured itself as a Python package that can be installed - and it will only work if you run the main program of the folder where it is (and there, in that folder has the folder data
, but for Python it is a package independent of your project).
Ideally, your project directory has a __init__.py
, and you work from a folder above, where with a minimum setup.py
file, you can type pip install --editable .
so that your game is seen as "whole". In this case, the imports will change to meujogo.data.creatures....
, or with a "."on the front - .data.creatures...
- but the whole project gets a" feeling " of a single product - it's much cooler.
O setup.py what you need is something like:
from setuptools import setup
setup(
name='route_optimizer',
version='0.1',
author='Thomas Caio',
py_packages=['meujogo'],
install_requires=[],
)
A simple way to do imports is to import all files from the folder:
from data.creatures.monster_list import *
The problem is that this would import the files and to access the classes would need to do Arquivo.Classe
. If that's not a problem for you, it's a great solution.