Is there a way for me to DRY common imports in my Python files?
Asked Answered
G

1

7

In C# the concept of global using can be used to DRY (do not repeat yourself) many common using statements across many files.

Now we have many similar imports across many Python files. Is there a similar strategy that we can use to reduce our boilerplate code?

Guard answered 15/8, 2023 at 13:0 Comment(1)
Not an answer but an observation: if you have a whole bunch of boilerplate imports it might be that you have a lot of low level complexity that’s being reimplemented in a bunch of different modules. Part of DRY is identifying those patterns and turning them into reusable abstractions, which in turn will shrink the number of names you need to import.Tithe
D
13

You could create a "prelude" module whose only purpose is to be glob imported by other modules. This idea can be found in other programming languages, like std::prelude from Rust and Prelude from Haskell.

For example, create a prelude.py file with contents like

from .sprockets import LeftSprocket, RightSprocket, make_sprocket
from .widgets import FooWidget, BarWidget
from .util import sprocket2widget, widget2sprokect

Then from whatever script you want to use it from, import it as

from my_package.my_module.prelude import *

All of the names from the prelude (LeftSprocket, FooWidget, etc.) will be immediately available.

This approach has some nice advantages:

  • It's entirely optional to the end users. Users can freely decide between using the prelude vs explicitly importing the modules they need.
  • It's non-invasive to the rest of your package. Providing a prelude doesn't require modifying any other modules or otherwise re-organizing your package.

That said, I wouldn't necessarily recommend creating preludes for all your projects. There are some significant disadvantages that it comes with:

  • There's now multiple modules that export the same names, so IDEs may have a harder time automatically completing imports. You may end up with your IDE suggesting things like my_package.my_module.prelude import FooWidget instead of my_package.my_module.widget import FooWidget. This can also be confusing to users, who won't know if the FooWidgets from both modules are the same or different without investigating both.

  • It makes it less obvious where global names are coming from. If you glob-import a prelude, there's no easy way to tell what names are available and what modules they come from. This goes against the Zen of Python, Explicit is better than implicit.

  • There's a risk of name collisions. Consider for example this recent question. The issue there had to do with the OP including both the imports from PIL import Image and from tkinter import *, where the latter silently re-defined Image to be tkinter.Image.

See also Why is "import *" bad?.

Debose answered 15/8, 2023 at 13:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.