简体   繁体   中英

Should I always use the most pythonic way to import modules?

I am making a tiny framework for games with pygame, on which I wish to implement basic code to quickly start new projects. This will be a module that whoever uses should just create a folder with subfolders for sprite classes, maps, levels, etc. My question is, how should my framework module load these client modules? I was considering to design it so the developer could just pass to the main object the names of the directories, like:

game = Game()
game.scenarios = 'scenarios'

Then game will append 'scenarios' to sys.path and use __import__() . I've tested and it works . But then I researched a little more to see if there were already some autoloader in python, so I could avoid to rewrite it, and I found this question Python modules autoloader? Basically, it is not recommended to use a autoloader in python, since "explicit is better than implicit" and "Readability counts".

That way, I think, I should compel the user of my module to manually import each of his/her modules, and pass these to the game instance, like:

import framework.Game
import scenarios
#many other imports
game = Game()
game.scenarios = scenarios
#so many other game.whatever = whatever

But this doesn't looks good to me, not so confortable. See, I am used to work with php, and I love the way it works with it's autoloader. So, the first exemple has some problability to crash or be some trouble, or is it just not 'pythonic'?

note: this is NOT an web application

I wouldn't consider letting a library import things from my current path or module good style. Instead I would only expect a library to import from two places:

  1. Absolute imports from the global modules space, like things you have installed using pip . If a library does this, this library must also be found in its install_requires=[] list

  2. Relative imports from inside itself. Nowadays these are explicitly imported from . :

     from . import bla from .bla import blubb 

This means that passing an object or module local to my current scope must always happen explicitly:

from . import scenarios
import framework

scenarios.sprites  # attribute exists
game = framework.Game(scenarios=scenarios)

This allows you to do things like mock the scenarios module:

import types
import framework

# a SimpleNamespace looks like a module, as they both have attributes
scenarios = types.SimpleNamespace(sprites='a', textures='b')
scenarios.sprites  # attribute exists
game = framework.Game(scenarios=scenarios)

Also you can implement a framework.utils.Scenario() class that implements a certain interface to provide sprites , maps etc. The reason being: Sprites and Maps are usually saved in separate files: What you absolutely do not want to do is look at the scenarios 's __file__ attribute and start guessing around in its files. Instead implement a method that provides a unified interface to that.

class Scenario():
    def __init__(self):
        ...

    def sprites(self):
        # optionally load files from some default location
        # If no such things as a default location exists, throw a NotImplemented error
        ...

And your user-specific scenarios will derive from it and optionally overload the loading methods

import framework.utils
class Scenario(framework.utils.Scenario):
    def __init__(self):
        ...

    def sprites(self):
        # this method *must* load files from location
        # accessing __file__ is OK here
        ...

What you can also do is have framework ship its own framework.contrib.scenarios module that is used in case no scenarios= keyword arg was used (ie for a square default map and some colorful default textures)

from . import contrib

class Game()
    def __init__(self, ..., scenarios=None, ...):
        if scenarios is None:
            scenarios = contrib.scenarios
        self.scenarios = scenarios

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM