2025, Oct 21 10:00

Importing a module and specific symbols in Python: what’s allowed, why it matters, and clean patterns

Learn why Python disallows importing a module and a symbol in one line. See dataclasses/json examples and a clean workaround with centralized imports.

When mixing import styles in Python, it’s tempting to keep both import module and from module import name for the same library. The motivation is usually clarity: call json.dumps and dataclasses.asdict with a module prefix to avoid ambiguity, while keeping the decorator @dataclass short and readable. The question is whether Python allows pulling in both the module itself and a specific symbol from it in a single line.

Example that illustrates the tension

import dataclasses
from dataclasses import dataclass
import json
@dataclass
class Vector3:
    i: int
    j: int
    k: int
    def as_json(self) -> str:
        return json.dumps(dataclasses.asdict(self))

Here, json.dumps and dataclasses.asdict are qualified to stay explicit, while @dataclass is used directly because it reads well and is clearly associated with the dataclasses module.

What the language actually allows

The concise answer is straightforward and definitive: there is no single statement in Python that both imports a module and simultaneously imports a symbol from that same module. You cannot replace the pair of statements with something like a hypothetical from dataclasses import self, dataclass. The form you already have is the correct one according to the syntax of the import statement.

no, per The import statement - Python Language Reference. What you already have is correct.

A practical workaround if you really want a single import site

If you still want a single line per importing module, one workaround is to centralize imports in a dedicated module and then import from it elsewhere. This doesn’t change Python’s syntax; it just moves the duplication to one place.

First, create a module that aggregates the imports:

# Contents of `hub_imports.py`
import dataclasses
from dataclasses import dataclass
import json

Then, consume these in your code:

from hub_imports import dataclasses, dataclass, json
@dataclass
class Node3D:
    a: int
    b: int
    c: int
    def to_json_text(self) -> str:
        return json.dumps(dataclasses.asdict(self))
print(Node3D(a=1, b=2, c=3).to_json_text())

This pattern gives you a single import statement per module that uses these names, while retaining explicit module-qualification for calls that benefit from it.

Why this matters

Names like dataclass are not always unique to a single library; for instance, there is an alternative implementation in Pydantic. Being deliberate about which objects are imported directly and which are used with a module prefix can make code easier to audit and reason about. Centralizing allowed imports in one place can also give very explicit control over what is permitted across a codebase.

There’s one more subtlety worth keeping in mind: in dataclasses, the function name is asdict(), not as_dict(). Using the correct API name helps avoid avoidable runtime errors.

The centralization approach is conceptually similar to what some JavaScript codebases call “barrel files.” It can be useful, but it’s also a design choice that not everyone favors. Treat it as a possible tool, not a default rule.

Takeaways

Python doesn’t provide a one-liner to import both a module and a specific symbol from it. The idiomatic and correct approach is to use two separate import statements. If you want to standardize imports across a project or reduce repetition in individual modules, you can group them in a dedicated module and import from there. Keep explicit module-qualification for potentially ambiguous calls, prefer clarity for decorators and frequently used names, and ensure you’re calling the right APIs such as dataclasses.asdict().

The article is based on a question from StackOverflow by Dominik Kaszewski and an answer by simon.