1.3.1 · D5Python Intermediate

Question bank — Modules — import, from…import, as aliasing

1,486 words7 min readBack to topic

The mental model to keep loaded, from the parent note:

  • import module binds one name; everything else lives behind the dot.
  • from module import name copies the name into your own namespace.
  • import executes a module's top-level code exactly once, then caches it in sys.modules.
  • Python finds the file by scanning sys.path, first match wins.

True or false — justify

True or false: import math copies math.pi into your file so you can write pi.
False — import math binds only the name math; pi stays behind the dot, so you must write math.pi. Only from math import pi copies the bare name.
True or false: importing the same module three times runs its top-level code three times.
False — the first import runs it once and stores the module object in sys.modules; the next two imports just re-bind an existing object, running nothing.
True or false: import numpy as np makes a fresh copy of NumPy under the name np.
False — as only attaches a second label to the same module object; there is no copy, just a shorter nickname pointing at the identical thing.
True or false: after from math import sqrt, changing math.sqrt later would change your sqrt too.
False — you copied the reference at import time; your sqrt name now points at that function independently, so rebinding math.sqrt afterwards does not touch your local sqrt.
True or false: from module import * is fine as long as the module has no name that clashes with yours.
Technically it may work, but it's still discouraged: you lose track of where names came from, and any future edit to the module (a new public name) can silently start shadowing your variables.
True or false: a module and a package are the same thing with different names.
False — a module is a single .py file, while a package is a folder of modules marked by an __init__.py; they behave differently when imported.
True or false: sys.path is checked bottom-to-top, so the standard library wins over your own files.
False — it is scanned top-to-bottom and the script's own directory sits near the top, so your files can shadow standard-library modules.
True or false: import os.path gives you a bare name path you can call directly.
False — it still binds the top name os; you reach the submodule as os.path, exactly like any other dotted access.
True or false: two files that both import mymod each get their own private copy of mymod.
False — sys.modules holds one shared module object; both files see the same object, so a variable set in mymod by one file is visible to the other.

Spot the error

What's wrong: import math then print(pi).
pi was never bound in your namespace — only math was. It must be math.pi, or you must switch to from math import pi.
What's wrong: from math import sqrt as then trying to use it.
as requires a new name after it; import sqrt as with nothing following is a syntax error. Write from math import sqrt as s.
What's wrong: you save your file as random.py, then import random to shuffle a list and it fails.
Your own random.py sits first on sys.path and shadows the standard library; Python imports your empty file instead of the real one. Rename your file.
What's wrong: from datetime import datetime then datetime.datetime.now().
You already imported the class datetime, so it's just datetime.now(); the extra .datetime would look for an attribute that doesn't exist and fails.
What's wrong: import numpy as np, pandas expecting both aliased to short names.
Only numpy got the alias; pandas keeps its full name. as binds to the single name immediately before it, not to the whole line.
What's wrong: a module writes print("loading") at top level; you import it once and expect the message each time you call a function from it.
Top-level code runs at import time, not at call time, and only on the first import. Calling functions later prints nothing.
What's wrong: from statistics import * then defining your own mean = sum(x)/len(x), then calling median(data).
The * import already pulled in mean, and your definition silently overwrote it; worse, you can no longer tell which names came from statistics, making the code hard to read and debug.

Why questions

Why does math. appear before every call after import math but vanish after from math import sqrt?
import math binds only math, so its contents are reached through it; from math import sqrt copies the bare name into your namespace, so no prefix is needed. It's a direct consequence of the lookup rule.
Why is from module import * risky even in a program that works today?
It hides the origin of names and can silently overwrite your variables; a future update adding a new public name to the module could clobber something of yours without any warning.
Why does the community standardise aliases like np, pd, plt?
A shared nickname makes code instantly readable across projects and tutorials — everyone recognises np.array — while still pointing at the exact same module object, following the DRY spirit of not reinventing conventions.
Why does Python cache modules in sys.modules instead of re-running them?
Re-running would repeat expensive side effects (connections, prints, setup) and could create inconsistent duplicate objects; caching makes imports fast and guarantees everyone shares one consistent module state.
Why does the order of folders in sys.path matter at all?
Because the first matching file wins, the ordering decides which of two same-named modules you actually get — this is exactly why a local random.py beats the standard library.
Why prefer import math over from math import * for a large program?
The dotted prefix keeps your namespace clean and makes every use self-documenting (you see math.sqrt), whereas * floods your namespace and erases that traceability.

Edge cases

Edge case: what happens if you import mymod twice on two lines — does the second line do anything?
It re-binds the name mymod to the already-cached object; no top-level code runs, so effectively nothing observable happens on the second line.
Edge case: a module has a syntax error only in a function you never call — will import succeed?
Yes — import runs top-level code, and a function body isn't executed until called, so a bug hidden inside an unused function won't stop the import (it fails only when you call that function).
Edge case: you from mod import a, b but mod defines only a — what happens?
The import fails with an ImportError on b; because a and b are requested together, the failed name aborts the whole statement, so you don't reliably get a either.
Edge case: an empty .py file — is it a valid module you can import?
Yes — a module needs no content; importing an empty file simply runs zero top-level lines and binds a module object with no useful names inside.
Edge case: module A imports B, and B imports A (circular import) — what's the danger?
Because each import runs top-level code once and only partially completes while the other is loading, a name referenced too early may not exist yet, causing an ImportError or AttributeError mid-load.
Edge case: does import care whether you're in a script vs an interactive shell for where it looks first?
The idea is the same — the current/script directory sits at the front of sys.path; in a shell that front entry is the current working directory, so a file there can still shadow a standard module.
Edge case: you set a variable inside mymod from main.py via mymod.x = 5; does another module importing mymod see the change?
Yes — all importers share the one cached module object, so mutating mymod.x in one place is visible everywhere mymod is imported.