Python Part I · Module 13 · Modules, Packages and Imports
import, from ... import, and Naming What You Borrow
Chapter 13.1 used one form of the statement, which brings in a module and leaves its contents behind the dot. There is a second form that reaches in and takes a specific name out, putting it directly into your file. Both are common, both appear in the first few lines of almost every script you will inherit, and the choice between them is not a style preference — it decides whether a reader three hundred lines down can tell where a name came from. It also decides how exposed you are to one specific failure, which produces no error and no warning at all.
Foundation15 min readPythonPart IFundamentalsModulesimport
Part I · Module 13 · Chapter 13.2 · import, from ... import, and Naming What You Borrow
1. The Two Forms
import math
from math import sqrt
print(math.sqrt(16))
print(sqrt(16))4.0
4.0Same answer, two routes.
import mathbinds one name,math, and everything inside stays behind the dot.from math import sqrtbinds one name,sqrt, directly in your file. The namemathis not bound at all.
The second form takes several names at once:
from math import sqrt, floor, pi
print(sqrt(25), floor(2.9), round(pi, 4))5.0 2 3.14162. from Does Not Give You the Module
A common first assumption, and it is wrong:
from math import sqrt
print(sqrt(16))
print(math.floor(3.7))4.0
NameError: name 'math' is not definedThe first line worked and the second did not. from math import sqrt brought in exactly one name, and math was not it.
If you want both, ask for both — import math and from math import sqrt can coexist, and often do.
3. What Each One Costs the Reader
Consider the same call under each form, three hundred lines into a file you did not write:
data = json.loads(text) # clearly from the json module
data = loads(text) # from... where?json.loads carries its origin at the point of use. loads does not — the reader has to scroll to the import block to find out, and if the file imports six modules they may have to check several.
That cost is zero when the name is unmistakable:
path = Path("logs") / "axi.log" # Path is obviously pathlib's
counts = Counter(severities) # Counter is obviously collections'Nobody needs the module name to know what Path or Counter is.
4. The Rule
Use from X import Y when the borrowed name is unambiguous on its own. Use import X when it is generic.
| Name | Form | Why |
|---|---|---|
Path | from pathlib import Path | unmistakable |
Counter | from collections import Counter | unmistakable |
datetime | from datetime import datetime | unmistakable |
loads / dumps | import json | generic — many things load and dump |
open | import ... | generic, and shadows a built-in |
mean | import statistics | generic — could be anything |
The practical test: would this name mean something to a reader who has not seen the import block? If yes, take it directly. If no, keep the module in front of it.
import json
import statistics
from pathlib import Path
from collections import Counter
severities = ["error", "info", "error"]
print(json.dumps({"n": 3}))
print(statistics.mean([2, 4, 6]))
print(Path("logs") / "axi_smoke.log")
print(Counter(severities)["error"]){"n": 3}
4
logs/axi_smoke.log
2Note the path separator: pathlib uses whatever is correct for the system the code runs on, so this line prints with a forward slash on macOS and Linux and a backslash on Windows. That is the point of the module — Chapter 13.4 returns to it.
5. The Silent Collision
from X import Y puts Y into your file, and your file may already have a Y. Nothing warns you, in either direction.
Your definition overwriting the import:
from math import pow
def pow(a, b):
return "my own pow"
print(pow(2, 3))
print(type(pow))my own pow
<class 'function'>math.pow(2, 3) would be 8.0. The def ran after the import and took the name.
The import overwriting your definition:
def load(x):
return "my loader"
from json import loads as load
print(load("[1, 2]"))[1, 2]Here the import came second and won. The function defined three lines earlier is simply gone.
(The as in that second example renames an import as it arrives — Chapter 13.3's subject, used here only to set up the collision.)
6. from X import *
A third form exists, brings in everything, and should almost never be written:
from math import *
print(sqrt(16), floor(3.9), round(pi, 2))4.0 3 3.14It works. The problem is what it does to Section 5: you cannot tell what arrived. math contributes dozens of names, and whether any of them collided with yours is not answerable by reading the file — the import line does not list them.
It also removes the benefit from Section 3 entirely. A reader seeing sqrt, gamma and hypot three hundred lines down has no line to check at all.
7. Reading an Inherited Import Block
The canonical three lines, decoded:
import json
from pathlib import Path
from collections import Counter
record = {"test": "axi_smoke", "status": "PASS"}
log_path = Path("logs") / "axi_smoke.log"
severities = Counter(["error", "info", "error"])
print(json.dumps(record))
print(log_path.name, log_path.suffix)
print(severities.most_common(1)){"test": "axi_smoke", "status": "PASS"}
axi_smoke.log .log
[('error', 2)]| Line | Binds | Tells you |
|---|---|---|
import json | json | this file reads or writes JSON |
from pathlib import Path | Path | it builds or inspects file paths |
from collections import Counter | Counter | it tallies something |
Three lines, and before reading a single statement of logic you know the file handles structured data, touches the filesystem and counts occurrences of something. That is why the convention from Chapter 13.1 Section 8 puts them at the top: the import block is a summary somebody wrote whether they intended to or not.
8. Predict, Run, Observe
import math
from math import floor
# A
print("A:", math.floor(5.9), floor(5.9))
# B
print("B:", "floor" in dir(), "math" in dir())
# C
from collections import Counter
print("C:", Counter("aab")["a"])
# D
def ceil(x):
return "mine"
from math import ceil
print("D:", ceil(1.2))The verified output:
A: 5 5
B: True True
C: 2
D: 2A — both forms reach the same function.
B — both names are bound here, because both forms were used.
C — Counter on a string counts its characters, since a string is a collection of characters (Chapter 10.3).
D — 2, not "mine". The import ran after the def and took the name. Section 5, in four lines.
9. Exercises
Exercise 1 — both forms. Use math.sqrt and a directly imported sqrt in one file. Then remove the plain import math and say which line breaks and why.
Exercise 2 — choose the form. For each, say which form you would use and why in one sentence: (a) statistics.median (b) pathlib.Path (c) json.dumps (d) datetime.datetime (e) random.choice.
Exercise 3 — the name is gone. Write from math import sqrt and then try to use math.pi. Record the error and explain which name was never bound.
Exercise 4 — collide, twice. Reproduce both directions of Section 5 — your definition winning, and the import winning. Say what decides the winner in one sentence.
Exercise 5 — make it safe. Take the collision where the import won and fix it two ways: by changing the form, and by renaming. Say which you prefer.
Exercise 6 — star import. Use from math import * and then check whether pi, e and tau are all available. Say what a reader of this file cannot determine.
Exercise 7 — decode a block. For a file starting with import csv, import statistics and from pathlib import Path, write one sentence predicting what the file does.
Exercise 8 — write the block. A script reads a JSON results file, counts severities, computes a mean runtime and writes a report path. Write its four import lines, choosing the form for each.
10. Summary
import Xbinds the module's name; everything stays behind the dot.from X import YbindsYdirectly and does not bindX.- Both can be used together, and often are.
- The dotted form carries its origin at every use site; the direct form does not.
- Use
fromwhen the name is unambiguous —Path,Counter,datetime. Useimport Xwhen it is generic —loads,mean,open. - The test is whether the name means something to a reader who has not seen the import block.
from X import Ycan collide with your own names, silently, in both directions. Whichever binding runs last wins — Chapter 2.3's rule, unchanged.import Xcannot collide that way, which makes it the safer default.from X import *makes collisions undetectable by reading. Meet it in old code; do not write it.- The import block is the file's dependency summary, and reading it tells you a great deal before any logic.
The next chapter covers the one piece of import syntax this chapter borrowed without explaining.
Where this fits
Part of the Python curriculum.
