Often in my own personal Python libraries, I do something like this:

class MyClass:

    # ...

    def plot(self):
        import someGraphicsLibrary as graphicslib

The reason is that initialising someGraphicsLibrary takes some time, up to a few seconds for one of the libraries I use. I don't always need to plot my results when I use this class, so it makes sense not to import it until the time it's actually used, if at all.

This seems to work fine, but I don't think I've seen it in anyone else's code. So my question is simply whether this is considered a good practice. Are there any hidden pitfalls to be expected when doing things this way?

  • 1
    This is simply a form of lazy loading, which is not very special. – Doc Brown Feb 13 at 6:54
  • 1
    Aside from perhaps being a little ugly, there is no real reason why you couldn't do this. And if the library import takes so much time, there is even a legitimate cause for doing so. I would just make a point to do this rarely. Most imports shouldn't be this slow. – Neil Feb 13 at 7:36
  • @DocBrown I'm not saying it's special, just asking if it's considered good/bad practice in Python specifically. – Nathaniel Feb 13 at 8:21
  • @Nathaniel: there seems to be some superstitious believe in the existence of absolute "best practices" among many devs - which is IMHO nonsense, any "practice" has pros and cons, and what is good or bad can only be evaluated in the specific context. Your example above looks reasonable to me, it is simple and clear, and the only real drawback is, it gets harder to see all dependencies of a module at a glance (which is point #3 in Kevin's answer). So if you think the performance improvement is worth the hassle, then this practice is good, if not, this practice is bad. – Doc Brown Feb 13 at 21:30

This is not usually good practice, for several reasons:

  1. Most of the time, the method will be fast, but the first time you call it, it will be slow. That's a lot less predictable than a startup-time import.
  2. If the import fails for any number of reasons, you won't know it until you call the method at runtime.
  3. It's harder to see which modules your module depends on, because they are not listed at the top of the file.
  4. If a module literally takes "a few seconds" to import, it may be doing too much in its import-time logic. Import-time logic should generally do "just enough" to make the module usable, and should not go around creating heavy global objects. Occasionally such objects are necessary, but it should be carefully scrutinized.

However, sometimes this is a Good Idea, for example:

  1. When implementing an array-like, you may need to use numpy in your __array__() method. But you may not want to depend on numpy for the other functionality of your module, so it's better to just import numpy inside of __array__() to avoid taking the extra dependency when it's not needed. This does not suffer from problems 1 and 2 because numpy has already been imported once before (it's the thing that's calling __array__() in the first place!), and it does not suffer from problem 3 because numpy isn't a "real" dependency of your module.
  2. Before PEP 553, a breakpoint traditionally looked like this: import pdb; pdb.set_trace(). You don't want to put import pdb at the top of the module because the breakpoint is a temporary line of code that will be deleted, and moving the import far away would make that needlessly difficult. This was obsoleted when the builtin breakpoint() was added, so now you don't need an inline import.
  • 4
    You forgot to mention that the example posted by the OP looks like one which fits to the "Good idea #1" case in your answer. It looks like a class or module which has a core functionality which is independent from plotting and might be used without the graphics lib. – Doc Brown Feb 13 at 6:51
  • @DocBrown: MyClass isn't a meaningful noun, so I think you are jumping to conclusions. – Kevin Feb 13 at 6:52
  • Well, I took the time to read the post in full. Maybe I did some guessing, but lets see what the OP thinks about it (and don't get me wrong, your answer already got an upvote from me). – Doc Brown Feb 13 at 6:55
  • @Kevin Doc Brown's first comment is a correct interpretation of my post. (+1, this is a helpful answer.) – Nathaniel Feb 13 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.