Intermediate Python 📂 Modules · 1 of 6 195 min read

Python Modules & Packages Tutorial

Master how Python code gets organised — modules, packages, imports, and everything in between. Learn the six import styles and when to use each, the name == "main" trick that makes a file both script and library, how Python actually finds your modules, absolute vs relative imports, init.py patterns, and virtual environments. Includes a visual package structure diagram and a hands-on example building a real reusable package.

🌐
SectionSection第 01 节

The Story That Explains Modules and Packages 🧰Modules और Packages समझाने वाली कहानी 🧰一个讲清模块与包的故事 🧰

The Hammer, the Toolbox and the Vanहथौड़ा, औज़ारों का डिब्बा और Van锤子、工具箱和货车
Imagine you are a carpenter. You start with a single hammer — one tool, one job. That is your first Python script: one file with a few functions, working on its own.

Soon you need saws, chisels and drills. You gather them into a toolbox — one container that keeps related tools together so you can carry them as a unit. That is a module: one .py file that bundles related functions, classes and constants.

Then you take jobs all over town. One toolbox is not enough — you need a van with labelled drawers: electrics, plumbing, framing. Each drawer is a toolbox of its own, and the van holds them all. That is a package: a folder of modules, organised so things are easy to find.

And when you hire a helper, you don't rebuild your tools — you say "grab the drill from the blue drawer." That is an import statement.
सोचिए आप एक बढ़ई हैं। आप एक हथौड़े से शुरू करते हैं — एक औज़ार, एक काम। यह है आपकी पहली Python script: कुछ functions वाली एक file, जो अपने-आप चलती है।

जल्द ही आपको आरी, छेनी और drill चाहिए। आप उन्हें एक औज़ारों के डिब्बे में रखते हैं — एक container जो संबंधित औज़ारों को साथ रखता है ताकि उन्हें एक इकाई की तरह उठाया जा सके। यह है module: एक .py file जो संबंधित functions, classes और constants को साथ बाँधती है।

फिर आप पूरे शहर में काम लेते हैं। एक डिब्बा काफ़ी नहीं — आपको लेबल लगे दराज़ों वाली van चाहिए: बिजली, plumbing, ढाँचा। हर दराज़ अपने-आप में एक डिब्बा है, और van सबको रखती है। यह है package: modules का एक folder, इस तरह सजाया गया कि चीज़ें आसानी से मिलें।

और जब आप किसी सहायक को रखते हैं, तो अपने औज़ार दोबारा नहीं बनाते — आप कहते हैं "नीले दराज़ से drill ले आओ।" यही है import statement।
想象你是一名木匠。你从一把 锤子 起步 — 一件工具,一项工作。这就是你的第一个 Python 脚本:一个包含几个函数、独立运行的文件。

很快你需要锯子、凿子和电钻。你把它们装进一个 工具箱 — 一个把相关工具放在一起、可以整体搬运的容器。这就是 模块:一个把相关函数、类和常量打包在一起的 .py 文件。

后来你在全城接活。一个工具箱不够了 — 你需要一辆带标签抽屉的 货车:电工、水管、木构。每个抽屉本身就是一个工具箱,货车装下所有抽屉。这就是 包:一个由模块组成、便于查找的文件夹。

当你雇了帮手,你不会重新造工具 — 你只说 "去蓝色抽屉拿电钻。"这就是 import 语句。

A module is a single Python file. A package is a folder of modules. The import statement is how one file borrows code from another. These three ideas let you grow from one script to a whole system.

Module एक Python file है। Package modules का एक folder है। import statement वह तरीका है जिससे एक file दूसरी से code उधार लेती है। ये तीन विचार आपको एक script से पूरे system तक ले जाते हैं।

模块 是一个 Python 文件。包 是由模块组成的文件夹。import 语句是一个文件向另一个文件借用代码的方式。这三个概念让你从一个脚本成长为一整个系统。

Script, Module, PackageScript, Module, Package脚本、模块、包
script, module, packagegame.py🔨 script — you run it🔨 script — आप चलाते हैं🔨 脚本 — 你运行它tools.py🧰 module — others import it🧰 module — दूसरे import करते हैं🧰 模块 — 别人导入它shop/🚚 package — a folder of modules🚚 package — modules का folder🚚 包 — 模块的文件夹cart.py · pay.pyimportimport

The same .py file can be a script (when you run it) or a module (when another file imports it). A package groups modules in a folder.

वही .py file script हो सकती है (जब आप उसे चलाएँ) या module (जब दूसरी file उसे import करे)। Package modules को एक folder में समूह बनाता है।

同一个 .py 文件既可以是脚本(你运行它时),也可以是模块(被别的文件导入时)。包把模块组织在一个文件夹里。

💡
The Core Insightमूल बात核心要点

Every Python file already is a module — you don't declare anything. The moment you save it, another file in the right place can import it. A package is a folder, usually with an __init__.py file inside that marks it as a package. That is the whole model.

हर Python file पहले से ही एक module है — आपको कुछ घोषित नहीं करना। Save करते ही, सही जगह पर रखी दूसरी file उसे import कर सकती है। Package एक folder है, आमतौर पर उसके अंदर एक __init__.py file होती है जो उसे package के रूप में चिह्नित करती है। बस यही पूरा मॉडल है।

每个 Python 文件 本来就是 模块 — 不需要任何声明。一保存,放在合适位置的其他文件就能 import 它。包是一个文件夹,通常里面有一个 __init__.py 文件,标记它是包。整个模型就这么简单。

▶️
This Tutorial Has Four Simulators and a Quizइस Tutorial में चार Simulators और एक Quiz है本教程有四个模拟器和一个小测验

Look for the interactive boxes: the __name__ Tracer (Section 05), the Import Path Lab (10), the Search Path Walk (11) and the Circular Import Tracer (15). Each one is built from real Python runs on real files — even the line-by-line steps were recorded with Python's own tracer.

Interactive डिब्बे खोजें: __name__ Tracer (Section 05), Import Path Lab (10), Search Path Walk (11) और Circular Import Tracer (15)। हर एक असली files पर असली Python runs से बना है — line-by-line कदम भी Python के अपने tracer से दर्ज किए गए हैं।

请留意这些互动框:__name__ Tracer(第 05 节)、Import Path Lab(10)、Search Path Walk(11)和 Circular Import Tracer(15)。每一个都基于真实文件上的真实 Python 运行 — 连逐行步骤都是用 Python 自带的跟踪器记录的。


SectionSection第 02 节

Script vs Module vs Package 🗂️Script बनाम Module बनाम Package 🗂️脚本、模块与包 🗂️

📄
Script
a .py file you runएक .py file जिसे आप चलाते हैं你运行的 .py 文件
A file you start with python file.py. It runs from top to bottom. Think of it as "the entry point" — the file that kicks everything off.
वह file जिसे आप python file.py से शुरू करते हैं। यह ऊपर से नीचे चलती है। इसे "entry point" समझें — वह file जो सब कुछ शुरू करती है।
用 python file.py 启动的文件。从上到下运行。把它看作"入口" — 启动一切的那个文件。
🧰
Module
a .py file others importएक .py file जिसे दूसरे import करते हैं被别人导入的 .py 文件
A file that offers reusable code — functions, classes, constants — through import module_name. Physically the same as a script; only its role differs.
वह file जो दोबारा इस्तेमाल होने वाला code देती है — functions, classes, constants — import module_name से। देखने में script जैसी; सिर्फ़ उसकी भूमिका अलग है।
通过 import module_name 提供 可复用代码 — 函数、类、常量 — 的文件。物理上与脚本相同,只是 角色 不同。
📦
Package
a folder of modulesmodules का एक folder模块的文件夹
A folder Python treats as a namespace. It holds modules and maybe sub-packages. A regular package has an __init__.py; without one, Python 3.3+ treats it as a "namespace package".
वह folder जिसे Python एक namespace मानता है। इसमें modules और शायद sub-packages होते हैं। सामान्य package में __init__.py होती है; उसके बिना Python 3.3+ इसे "namespace package" मानता है।
Python 视为命名空间的文件夹,里面有模块,也可能有子包。普通包包含 __init__.py;没有它时,Python 3.3+ 把它当作"命名空间包"。

SectionSection第 03 节

Your First Module — A 90-Second Walkthrough ⏱️आपका पहला Module — 90 सेकंड में ⏱️你的第一个模块 — 90 秒上手 ⏱️

The smallest possible module: two files in the same folder. Then run python main.py.

सबसे छोटा module: एक ही folder में दो files। फिर python main.py चलाएँ।

最小的模块:同一文件夹中的两个文件。然后运行 python main.py。

📄 math_tools.py
# math_tools.py
PI = 3.14159

def area_circle(radius):
    return PI * radius ** 2

def area_square(side):
    return side ** 2
📄 main.py
# main.py
import math_tools

print(math_tools.area_circle(5))
print(math_tools.area_square(4))
print(math_tools.PI)
OUTPUT
78.53975 16 3.14159

That's it. Because math_tools.py sits in the same folder as the script you ran, import math_tools finds it. No configuration, no build step. Section 11 shows exactly where Python looks.

बस इतना ही। क्योंकि math_tools.py उसी folder में है जहाँ चलाई गई script है, import math_tools उसे ढूँढ लेता है। कोई configuration नहीं, कोई build step नहीं। Section 11 ठीक-ठीक दिखाता है कि Python कहाँ-कहाँ देखता है।

就这样。因为 math_tools.py 和你运行的脚本在同一个文件夹,import math_tools 就能找到它。不需要配置,也没有构建步骤。第 11 节会准确展示 Python 到哪些地方查找。


SectionSection第 04 节

The Six Import Styles — Know All of Them 📥Import के छह तरीके — सब जानें 📥六种导入写法 — 全都要会 📥

📄 main.py
# 1) Import the whole module - use it with a dot
import math
print(math.sqrt(16))

# 2) Import with an alias - handy for long names (numpy is installed with pip)
import numpy as np
print(np.array([1, 2, 3]) * 2)

# 3) Import specific names - no dot needed
from math import sqrt, pi
print(sqrt(16), pi)

# 4) Import a name with an alias
from datetime import date as dt
print(dt(2026, 1, 26).strftime("%d %b %Y"))

# 5) Import everything - avoid outside quick experiments
from math import *
print(floor(7.9))          # works, but where did floor come from?

# 6) Import from a module inside a package
from os.path import join
print(join("data", "file.csv"))
OUTPUT
4.0 [2 4 6] 4.0 3.141592653589793 26 Jan 2026 7 data/file.csv

Style 2 uses numpy, a third-party package — install it first with python -m pip install numpy (Section 14). All the others are in the standard library.

तरीका 2 numpy इस्तेमाल करता है, जो third-party package है — पहले python -m pip install numpy से install करें (Section 14)। बाक़ी सब standard library में हैं।

写法 2 用到第三方包 numpy — 先用 python -m pip install numpy 安装(第 14 节)。其余都在标准库里。

Styleतरीका写法Best forकिसके लिए अच्छा最适合Watch out forध्यान रखें注意
import xClear origin — you always see x.func()साफ़ स्रोत — हमेशा x.func() दिखता है来源清晰 — 总能看到 x.func()Long module names get wordyलंबे module नाम भारी लगते हैं模块名长时显得啰嗦
import x as yLong names (numpy, pandas) or clashesलंबे नाम (numpy, pandas) या टकराव长名字(numpy、pandas)或名字冲突Use the standard aliases (np, pd)मानक aliases इस्तेमाल करें (np, pd)使用通行别名(np、pd)
from x import aNames you use a lot; short callsबार-बार इस्तेमाल होने वाले नाम; छोटी calls常用的名字;调用更短Loses the "which module is this from?" hint"यह किस module से है?" का संकेत खो जाता है失去"它来自哪个模块?"的提示
from x import a as bRenaming to avoid a clashटकराव से बचने के लिए नाम बदलना改名以避免冲突Confuses readers unless the alias is commonAlias आम न हो तो पढ़ने वाले उलझते हैं别名不常见时会让读者困惑
from x import *Quick experiments in the REPL onlyसिर्फ़ REPL में जल्दी के प्रयोग仅限 REPL 中快速试验Silently overwrites names (see below)चुपचाप नाम overwrite करता है (नीचे देखें)悄悄覆盖名字(见下文)
from x.y import aReaching into a packagePackage के अंदर पहुँचना深入包内部Very deep paths suggest poor package designबहुत गहरे paths ख़राब package design का संकेत路径太深说明包设计欠佳

Why from module import * Is Risky — Prooffrom module import * जोखिम भरा क्यों — सबूत为什么 from module import * 有风险 — 证明

📄 cleaning.py
def process(data):
    return [x.strip() for x in data]
📄 billing.py
def process(data):
    return sum(data)
📄 main.py
from cleaning import *
from billing import *     # silently replaces process!

print(process.__module__)
print(process([" a ", " b "]))
OUTPUT
billing Traceback (most recent call last): File "/home/aarav/myproject/main.py", line 5, in <module> print(process([" a ", " b "])) ~~~~~~~^^^^^^^^^^^^^^^^ File "/home/aarav/myproject/billing.py", line 2, in process return sum(data) TypeError: unsupported operand type(s) for +: 'int' and 'str'
⚠️
Two Modules, One Nameदो Modules, एक नाम两个模块,一个名字

Both modules define process(). The second star import silently replaced the first — no warning — so the cleaning code ended up calling the billing function and crashed. With explicit imports (import cleaning, then cleaning.process()) this can't happen, and a code search finds every use.

दोनों modules process() बनाते हैं। दूसरे star import ने पहले वाले को चुपचाप बदल दिया — कोई चेतावनी नहीं — इसलिए सफ़ाई वाले code ने billing function बुला दिया और crash हुआ। Explicit imports (import cleaning, फिर cleaning.process()) में ऐसा नहीं हो सकता, और code search हर इस्तेमाल ढूँढ लेता है।

两个模块都定义了 process()。第二个星号导入悄悄替换了第一个 — 没有任何警告 — 于是清洗代码调用了计费函数并崩溃。使用显式导入(import cleaning,再 cleaning.process())就不会这样,代码搜索也能找到每一处用法。


SectionSection第 05 节

The if __name__ == "__main__" Idiom 🚪if __name__ == "__main__" का मुहावरा 🚪if __name__ == "__main__" 惯用法 🚪

Every module has a variable called __name__. When you run a file directly, its __name__ is "__main__". When you import it, __name__ is the module's name. This one difference lets a file be both a script and a module. Here is one file, run in two different ways:

हर module में __name__ नाम का variable होता है। जब आप file को सीधे चलाते हैं, उसका __name__ "__main__" होता है। जब आप उसे import करते हैं, __name__ module का नाम होता है। यही एक फ़र्क़ file को script और module दोनों बनने देता है। यह एक file है, दो अलग तरीकों से चलाई गई:

每个模块都有一个名为 __name__ 的变量。直接 运行 文件时,它的 __name__ 是 "__main__";被 导入 时,__name__ 是模块名。正是这一点区别,让一个文件既能当脚本 又能 当模块。下面是同一个文件的两种运行方式:

📄 math_tools.py
# math_tools.py
def area_circle(radius):
    return 3.14159 * radius ** 2

def demo():
    print("Testing area_circle:")
    print(area_circle(5))
    print(area_circle(10))

print("__name__ is", repr(__name__))

# This block ONLY runs when you execute the file directly
if __name__ == "__main__":
    demo()
📄 main.py
import math_tools
print("main.py got", math_tools.area_circle(2))
OUTPUT
$ python math_tools.py __name__ is '__main__' Testing area_circle: 78.53975 314.159 $ python main.py __name__ is 'math_tools' main.py got 12.56636
🧠 Two Ways the Same File Behavesएक ही File के दो व्यवहार同一文件的两种表现
Run
python math_tools.py — __name__ is "__main__", so the demo runs. python math_tools.py — __name__ "__main__" है, इसलिए demo चलता है। python math_tools.py — __name__ 是 "__main__",所以演示代码运行。
Import
import math_tools — __name__ is "math_tools", so the demo does not run. The top-level print still runs! import math_tools — __name__ "math_tools" है, इसलिए demo नहीं चलता। Top-level print फिर भी चलता है! import math_tools — __name__ 是 "math_tools",所以演示代码 不 运行。但顶层的 print 仍会运行!

Try It — __name__ Tracerकरके देखें — __name__ Tracer动手试试 — __name__ 跟踪器

Choose how the file is started, and whether it has the guard. Step through and watch which lines Python actually runs — the steps were recorded line by line with Python's tracer. Try no guard + imported to see the problem the guard solves.

चुनें कि file कैसे शुरू होती है, और उसमें guard है या नहीं। कदम-दर-कदम चलें और देखें Python असल में कौन-सी lines चलाता है — कदम Python के tracer से line-by-line दर्ज किए गए हैं। Guard किस समस्या को हल करता है, यह देखने के लिए no guard + imported आज़माएँ।

选择文件的启动方式,以及是否有守卫。逐步执行,看 Python 实际运行了哪些行 — 这些步骤是用 Python 跟踪器逐行记录的。试试 no guard + imported,看看守卫解决的是什么问题。

🚪 __name__ TracerSame file, two roles. The guard decides what runs.एक file, दो भूमिकाएँ। Guard तय करता है क्या चले।同一文件,两种角色。守卫决定运行什么。
startशुरुआत启动
guard
🔑
Why Reusable Files Need This Guardदोबारा इस्तेमाल होने वाली Files को यह Guard क्यों चाहिए为什么可复用文件需要这个守卫

Without the guard, all top-level code runs the moment another file imports yours: tests, prints, slow setup. The guard says "only run this if I am the program being launched." It is how libraries stay quiet when imported. Keep the top level of a module to definitions — functions, classes, constants.

Guard के बिना, जैसे ही दूसरी file आपकी file import करती है, सारा top-level code चल जाता है: tests, prints, धीमी तैयारी। Guard कहता है "इसे तभी चलाओ जब मैं ही शुरू किया गया program हूँ।" इसी से libraries import होने पर चुप रहती हैं। Module के top level पर सिर्फ़ परिभाषाएँ रखें — functions, classes, constants।

没有守卫,别的文件一导入你的文件,所有顶层代码就会运行:测试、打印、缓慢的初始化。守卫的意思是 "只有我是被启动的那个程序时才运行这段。"库就是靠它在被导入时保持安静。模块顶层只放定义 — 函数、类、常量。


SectionSection第 06 节

Imports Run Once — The Module Cache 🗄️Import एक ही बार चलता है — Module Cache 🗄️导入只运行一次 — 模块缓存 🗄️

The first import of a module runs its file and stores the module object in a dictionary called sys.modules. Every later import of the same name just looks it up there — the file is not run again.

किसी module का पहला import उसकी file चलाता है और module object को sys.modules नाम की dictionary में रख देता है। उसी नाम का हर अगला import बस वहाँ देखता है — file दोबारा नहीं चलती।

首次 import 某个模块时,会运行它的文件,并把模块对象存进名为 sys.modules 的字典。之后再导入同名模块,只是在那里查找 — 文件 不会 再次运行。

📄 noisy.py
print("  ...loading noisy.py")
COUNT = 0
📄 main.py
import sys
import importlib

import noisy
import noisy                 # second import: nothing printed
noisy.COUNT += 1

print("noisy" in sys.modules)
print(noisy.COUNT)

importlib.reload(noisy)      # force a fresh run of the file
print(noisy.COUNT)
OUTPUT
...loading noisy.py True 1 ...loading noisy.py 0
🔁
Why This Mattersयह क्यों मायने रखता है为什么这很重要

Importing is cheap after the first time, and every file that imports noisy shares the same module object — a change made through one import (like COUNT += 1) is seen by all of them. importlib.reload() runs the file again, which is handy in an interactive session but rarely needed in programs.

पहली बार के बाद import सस्ता है, और noisy import करने वाली हर file एक ही module object साझा करती है — एक import से किया गया बदलाव (जैसे COUNT += 1) सभी को दिखता है। importlib.reload() file को फिर से चलाता है, जो interactive session में काम का है पर programs में कम ही चाहिए।

第一次之后导入就很便宜,而且每个导入 noisy 的文件共享 同一个 模块对象 — 通过一处导入所做的修改(如 COUNT += 1)所有人都能看到。importlib.reload() 会重新运行文件,在交互式会话中很方便,但程序里很少需要。


SectionSection第 07 节

Packages — Modules Grow Up 📦Packages — Modules बड़े होते हैं 📦包 — 模块长大了 📦

When one file grows into ten, group them in a folder — a package. Packages let you organise code by feature, share it between projects and publish libraries.

जब एक file दस बन जाए, तो उन्हें एक folder में रखें — एक package। Packages से आप code को feature के हिसाब से सजा सकते हैं, projects में साझा कर सकते हैं और libraries publish कर सकते हैं।

当一个文件长成十个,就把它们放进一个文件夹 — 包。包让你按功能组织代码、在项目间共享、发布库。

A Typical Package Structureएक आम Package की बनावट典型的包结构
package tree📁mypackage/← the top-level package← top-level package← 顶层包⚙️__init__.py← marks the folder as a package← folder को package बताती है← 标记文件夹为包📄config.py← settings, constants← settings, constants← 设置、常量📄helpers.py← shared utility functions← साझा utility functions← 共享的工具函数📁core/← a sub-package← एक sub-package← 子包⚙️__init__.py← marks core/ as a package← core/ को package बताती है← 标记 core/ 为包📄engine.py← main logic← मुख्य logic← 主要逻辑📄utils.py← helpers for core← core के helpers← core 的辅助函数📄README.md← non-Python files are fine too← non-Python files भी चलती हैं← 非 Python 文件也可以

Amber folders are packages, ⚙️ files are __init__.py markers, blue files are ordinary modules.

नारंगी folders packages हैं, ⚙️ files __init__.py markers हैं, नीली files सामान्य modules हैं।

琥珀色文件夹是包,⚙️ 文件是 __init__.py 标记,蓝色文件是普通模块。

🧩
Is __init__.py Required?क्या __init__.py ज़रूरी है?__init__.py 是必需的吗?

Since Python 3.3, a folder without __init__.py can still be imported — it becomes a namespace package, a feature meant for splitting one package across several folders. For your own projects, add an __init__.py (it can be empty): it makes the folder a regular package, and many tools expect it.

Python 3.3 से, __init__.py के बिना folder भी import हो सकता है — वह namespace package बन जाता है, यह feature एक package को कई folders में बाँटने के लिए है। अपने projects में __init__.py ज़रूर रखें (ख़ाली भी चलेगी): इससे folder सामान्य package बनता है, और कई tools इसकी उम्मीद करते हैं।

从 Python 3.3 起,没有 __init__.py 的文件夹也能被导入 — 它成为 命名空间包,这个特性用于把一个包拆分到多个文件夹。在自己的项目里请加上 __init__.py(可以为空):它让文件夹成为普通包,很多工具也依赖它。


SectionSection第 08 节

Importing from a Package — Dotted Paths 🔗Package से Import — Dots वाले Paths 🔗从包中导入 — 点号路径 🔗

Inside a package, import uses dots to walk down the folders: each dot goes one level deeper. This main.py sits next to the mypackage/ folder from the diagram above:

Package के अंदर import folders में नीचे जाने के लिए dots इस्तेमाल करता है: हर dot एक स्तर गहरा जाता है। यह main.py ऊपर के चित्र वाले mypackage/ folder के बगल में है:

在包里,import 用 点号 逐级进入文件夹:每个点深入一层。下面的 main.py 与上图的 mypackage/ 文件夹位于同一层:

📄 main.py
# main.py - OUTSIDE the package
import mypackage.config
print(mypackage.config.DEBUG)

import mypackage.core.engine
print(mypackage.core.engine.run())

from mypackage.core import engine
print(engine.run())

from mypackage.core.engine import run
print(run())

from mypackage.core import engine as eng
print(eng.run())
OUTPUT
True engine running engine running engine running engine running

SectionSection第 09 节

The __init__.py File — What Goes in It? 🎁__init__.py File — इसमें क्या जाता है? 🎁__init__.py 文件 — 里面放什么?🎁

Most __init__.py files are empty. But __init__.py runs the first time the package is imported, so it is the perfect place to shape the package's public face: re-export the useful names and list them in __all__.

ज़्यादातर __init__.py files ख़ाली होती हैं। पर package के पहली बार import होने पर __init__.py चलती है, इसलिए यह package का सार्वजनिक चेहरा बनाने की बढ़िया जगह है: काम के नाम re-export करें और उन्हें __all__ में लिखें।

大多数 __init__.py 是空的。但包第一次被导入时 __init__.py 会运行,所以它是塑造包的 公开门面 的好地方:重新导出有用的名字,并在 __all__ 中列出它们。

📄 shop/__init__.py
# shop/__init__.py - the public face of the package
from .cart import add_item, total, _audit
from .config import VERSION

__all__ = ["add_item", "total", "VERSION"]
📄 shop/cart.py
_items = []

def add_item(name, price):
    _items.append((name, price))

def total():
    return sum(price for _, price in _items)

def _audit():
    return list(_items)
📄 main.py
from shop import add_item, total, VERSION     # short, thanks to __init__.py
add_item("pen", 20)
add_item("book", 150)
print("total:", total(), "| version:", VERSION)

from shop.cart import total as deep_total    # the long path still works
print(deep_total())

ns = {}
exec("from shop import *", ns)               # what does * bring in?
print(sorted(k for k in ns if not k.startswith("__")))
OUTPUT
total: 170 | version: 2.1 170 ['VERSION', 'add_item', 'total']
📈
__all__ and the Leading Underscore__all__ और आगे का Underscore__all__ 与前导下划线

__all__ decides what from shop import * brings in — above, _audit stayed out. Without __all__, a star import skips every name that starts with _. Either way, nothing is truly private: from shop import _audit still works. A leading underscore is a polite signal — "internal, may change, don't rely on it."

__all__ तय करता है कि from shop import * क्या लाए — ऊपर _audit बाहर रहा। __all__ के बिना star import _ से शुरू होने वाला हर नाम छोड़ देता है। दोनों हालात में कुछ भी सच में private नहीं: from shop import _audit फिर भी चलता है। आगे का underscore एक विनम्र संकेत है — "अंदरूनी, बदल सकता है, इस पर निर्भर न रहें।"

__all__ 决定 from shop import * 会带进来什么 — 上面的 _audit 就没有被带进来。没有 __all__ 时,星号导入会跳过所有以 _ 开头的名字。无论哪种情况,都没有真正的私有:from shop import _audit 依然可用。前导下划线只是礼貌的信号 —"内部使用,可能会改,别依赖它。"


SectionSection第 10 节

Absolute vs Relative Imports 🧭Absolute बनाम Relative Imports 🧭绝对导入与相对导入 🧭

When code inside a package imports another part of the same package, you can spell out the full path (absolute) or use dots relative to the current file (relative).

जब package के अंदर का code उसी package का दूसरा हिस्सा import करे, तो आप पूरा path लिख सकते हैं (absolute) या मौजूदा file के हिसाब से dots इस्तेमाल कर सकते हैं (relative)।

当包 内部 的代码导入 同一 包的其他部分时,你可以写出完整路径(绝对),也可以用相对当前文件的点号(相对)。

📚 Absolute ImportAbsolute Import绝对导入
Traitगुण特性Behaviourव्यवहार表现
Syntaxfrom mypackage.core import utils
Reads likeऐसे पढ़ा जाता है读起来像A full addressपूरा पता完整地址
Renaming the packagePackage का नाम बदलना包改名Every import must changeहर import बदलना पड़ता है每处导入都得改
PEP 8Recommended by defaultDefault रूप से सुझाया गया默认推荐
🌱 Relative ImportRelative Import相对导入
Traitगुण特性Behaviourव्यवहार表现
Syntaxfrom . import utils
Reads likeऐसे पढ़ा जाता है读起来像"Next to me""मेरे बगल में""在我旁边"
Renaming the packagePackage का नाम बदलना包改名Keeps workingचलता रहता है仍然有效
Ruleनियम规则Only inside a packageसिर्फ़ package के अंदर只能在包内使用
📑 Dots in a Relative ImportRelative Import में Dots相对导入中的点号
.
This package — the folder this file is in: from . import utils यही package — वह folder जिसमें यह file है: from . import utils 当前包 — 本文件所在的文件夹:from . import utils
.foo
Module foo in this package: from .utils import tidy इसी package का module foo: from .utils import tidy 当前包中的模块 foo:from .utils import tidy
..
The parent package, one level up: from .. import config Parent package, एक स्तर ऊपर: from .. import config 父包,向上一级:from .. import config
..foo
Module foo in the parent package: from ..helpers import shout Parent package का module foo: from ..helpers import shout 父包中的模块 foo:from ..helpers import shout

Here is engine.py using all three forms, started in two different ways:

यह engine.py तीनों रूप इस्तेमाल करती है, दो अलग तरीकों से शुरू की गई:

下面的 engine.py 用了全部三种写法,并以两种方式启动:

📄 mypackage/core/engine.py
# mypackage/core/engine.py
from mypackage import config         # 1) absolute
from . import utils                  # 2) relative, same package (core/)
from .. import helpers               # 3) relative, parent package (mypackage/)

def run():
    return "engine running"

if __name__ == "__main__":
    print(config.VERSION, utils.tidy("  a   b  "), helpers.shout("go"))
OUTPUT
$ python -m mypackage.core.engine 1.0.0 a b GO! $ python mypackage/core/engine.py Traceback (most recent call last): File "/home/aarav/myproject/mypackage/core/engine.py", line 2, in <module> from mypackage import config # 1) absolute ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ModuleNotFoundError: No module named 'mypackage'
⚠️
Run Package Modules with -mPackage Modules को -m से चलाएँ用 -m 运行包中的模块

Started by its path, engine.py is just a script: Python puts mypackage/core/ at the front of sys.path, so it can't see mypackage at all — even the absolute import fails, and relative imports would fail with attempted relative import with no known parent package. Run it from the project folder with python -m mypackage.core.engine and everything works.

Path से शुरू करने पर engine.py बस एक script है: Python mypackage/core/ को sys.path में सबसे आगे रखता है, इसलिए वह mypackage को देख ही नहीं पाता — absolute import भी fail होता है, और relative imports attempted relative import with no known parent package के साथ fail होते। Project folder से python -m mypackage.core.engine चलाएँ और सब चलता है।

按路径启动时,engine.py 只是个脚本:Python 把 mypackage/core/ 放在 sys.path 最前面,因此根本看不到 mypackage — 连 绝对 导入都失败,相对导入则会报 attempted relative import with no known parent package。在项目文件夹中用 python -m mypackage.core.engine 运行,一切正常。

Try It — Import Path Labकरके देखें — Import Path Lab动手试试 — 导入路径实验室

Choose who is importing, what it imports and which style it uses. Each of the 18 combinations was really run on these files — the console shows Python's actual result.

चुनें कौन import कर रहा है, क्या import करता है और कौन-सा तरीका इस्तेमाल करता है। 18 में से हर जोड़ इन files पर सच में चलाया गया — console Python का असली नतीजा दिखाता है।

选择谁在导入、导入什么、用哪种写法。18 种组合都在这些文件上真实运行过 — 控制台显示的是 Python 的实际结果。

🧭 Import Path LabAbsolute or relative? It depends on HOW the file was started.Absolute या relative? यह इस पर निर्भर है कि file कैसे शुरू हुई।绝对还是相对?取决于文件是怎么启动的。
whoकौन谁
importsimport करे导入
styleतरीका写法

SectionSection第 11 节

How Python Finds a Module — The Search Path 🔎Python Module कैसे ढूँढता है — Search Path 🔎Python 如何找到模块 — 搜索路径 🔎

When you write import random, Python asks a fixed list of places, in order, and uses the first one that has it. The full order is:

जब आप import random लिखते हैं, Python जगहों की एक तय सूची से क्रम से पूछता है, और जिसके पास पहले मिले उसे इस्तेमाल करता है। पूरा क्रम यह है:

当你写 import random 时,Python 会 按顺序 询问一组固定的地方,用第一个找到的。完整顺序是:

Import Resolution OrderImport खोजने का क्रम导入解析顺序
import search order🗄️sys.modulesalready imported?पहले import हुआ?已导入过?›⚙️built-insys, math…sys, math…sys、math…›🧊frozenos, codecs…os, codecs…os、codecs…›📁sys.path[0]script's folderscript का folder脚本文件夹›🛣️PYTHONPATHextra foldersअतिरिक्त folders额外文件夹›📚stdlibrandom, json…random, json…random、json…›📦site-packagespip installspip installspip 安装的import randomFirst match wins → module loadedपहला match जीतता है → module load第一个匹配获胜 → 加载模块Not found anywhere → ModuleNotFoundErrorकहीं नहीं मिला → ModuleNotFoundError到处找不到 → ModuleNotFoundError

Steps 4–7 are the folders in sys.path, in order. Your script's folder comes before the standard library — that is why a file called random.py can hide the real random.

कदम 4–7 sys.path के folders हैं, क्रम से। आपकी script का folder standard library से पहले आता है — इसीलिए random.py नाम की file असली random को छिपा सकती है।

第 4–7 步是 sys.path 中按顺序排列的文件夹。脚本所在文件夹排在标准库 之前 — 这就是名为 random.py 的文件能遮住真正 random 的原因。

Try It — Search Path Walkकरके देखें — Search Path Walk动手试试 — 搜索路径之旅

Pick a module, then add a local file with the same name, or a PYTHONPATH folder. Press Play and watch Python walk the list. Every result — including the real file path in the console — comes from actually running Python 3.13 in that situation. Compare random, math and os with a local file: only one of them gets hijacked!

एक module चुनें, फिर उसी नाम की local file, या PYTHONPATH folder जोड़ें। चलाएँ दबाएँ और Python को सूची में चलते देखें। हर नतीजा — console का असली file path भी — उस स्थिति में Python 3.13 को सच में चलाकर आया है। Local file के साथ random, math और os की तुलना करें: इनमें से सिर्फ़ एक ही hijack होता है!

选一个模块,再加上同名的本地文件或 PYTHONPATH 文件夹。点 播放,看 Python 沿列表查找。每个结果 — 包括控制台中的真实文件路径 — 都来自在该情境下实际运行 Python 3.13。比较一下带本地文件时的 random、math 和 os:只有一个会被劫持!

🔎 Search Path WalkWhere does Python find it? First match wins.Python इसे कहाँ पाता है? पहला match जीतता है।Python 在哪里找到它?第一个匹配获胜。
import
local filelocal file本地文件PYTHONPATH

See Your Own Search Pathअपना Search Path देखें查看你自己的搜索路径

📄 main.py
import sys
for path in sys.path:
    print(path)
OUTPUT
/home/aarav/myproject /usr/lib/python313.zip /usr/lib/python3.13 /usr/lib/python3.13/lib-dynload /root/.local/lib/python3.13/site-packages /usr/local/lib/python3.13/dist-packages /usr/lib/python3/dist-packages

These are the folders on the machine that built this page — yours will differ, but the shape is the same: your project first, then the standard library, then the folders where pip installs packages.

ये उस मशीन के folders हैं जिसने यह page बनाया — आपके अलग होंगे, पर ढाँचा वही है: पहले आपका project, फिर standard library, फिर वे folders जहाँ pip packages install करता है।

这些是构建本页面的机器上的文件夹 — 你的会不同,但结构一样:先是你的项目,然后是标准库,最后是 pip 安装包的文件夹。

The Shadowing Trap — ProofShadowing का जाल — सबूत遮蔽陷阱 — 证明

📄 random.py
# random.py - oops, same name as the standard library module
print("my random.py was loaded!")
📄 game.py
import random
print(random.randint(1, 6))
OUTPUT
my random.py was loaded! Traceback (most recent call last): File "/home/aarav/myproject/game.py", line 2, in <module> print(random.randint(1, 6)) ^^^^^^^^^^^^^^ AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/aarav/myproject/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)
🔑
Python 3.13 Now Warns YouPython 3.13 अब आपको चेतावनी देता हैPython 3.13 现在会提醒你

Older versions only said module 'random' has no attribute 'randint', which is baffling. Since Python 3.13 the message adds "consider renaming … since it has the same name as the standard library module". Still, the rule stands: never name your files random.py, json.py, email.py, test.py or after any package you use.

पुराने versions बस module 'random' has no attribute 'randint' कहते थे, जो उलझाने वाला है। Python 3.13 से संदेश में "consider renaming … since it has the same name as the standard library module" जुड़ता है। फिर भी नियम वही है: अपनी files के नाम कभी random.py, json.py, email.py, test.py या किसी इस्तेमाल होने वाले package पर न रखें।

旧版本只会说 module 'random' has no attribute 'randint',让人摸不着头脑。从 Python 3.13 起,信息里会加上 "consider renaming … since it has the same name as the standard library module"。但规则不变:永远不要把文件命名为 random.py、json.py、email.py、test.py,或你使用的任何包的名字。


SectionSection第 12 节

Practical Example — Building a Small Package 🛠️व्यावहारिक उदाहरण — एक छोटा Package बनाना 🛠️实战示例 — 构建一个小包 🛠️

A real, minimal package for text analysis: four files, one clean public API. The folder looks like this:

Text analysis के लिए एक असली, छोटा package: चार files, एक साफ़ public API। Folder ऐसा दिखता है:

一个真实的、精简的文本分析包:四个文件,一个干净的公开 API。文件夹结构如下:

textstats/
    __init__.py
    _internal.py
    counter.py
    reader.py
speech.txt
main.py
📄 textstats/_internal.py
# Internal helpers - the leading underscore says "private"
import re

def _clean(text):
    """Remove punctuation, lowercase, collapse spaces."""
    text = re.sub(r"[^\w\s]", "", text.lower())
    return " ".join(text.split())
📄 textstats/counter.py
from collections import Counter

from ._internal import _clean       # relative import - same package

def word_count(text):
    """Total words in a piece of text."""
    return len(_clean(text).split())

def top_words(text, n=5):
    """The n most common words as (word, count) pairs."""
    return Counter(_clean(text).split()).most_common(n)
📄 textstats/reader.py
from pathlib import Path

def read_file(path):
    """Load the full text of a file."""
    return Path(path).read_text(encoding="utf-8")
📄 textstats/__init__.py
# Expose the useful names at the package top level
from .counter import word_count, top_words
from .reader import read_file

__version__ = "1.0.0"
__all__ = ["word_count", "top_words", "read_file"]
📄 main.py
import textstats

text = textstats.read_file("speech.txt")
print(textstats.word_count(text))
print(textstats.top_words(text, n=3))
print(textstats.__version__)
OUTPUT
29 [('learn', 3), ('it', 3), ('we', 2)] 1.0.0
✅
What Just Happenedअभी क्या हुआ刚才发生了什么

You built a real, importable package in four small files. Users see three clean function names; _internal._clean() stays out of sight. Files inside textstats/ import each other with relative imports, so the package can be renamed without editing them. Big libraries like requests and numpy are organised the same way — just with more files.

आपने चार छोटी files में एक असली, import होने लायक package बनाया। Users को तीन साफ़ function नाम दिखते हैं; _internal._clean() नज़र से दूर रहता है। textstats/ के अंदर की files relative imports से एक-दूसरे को import करती हैं, इसलिए package का नाम उन्हें बदले बिना बदला जा सकता है। requests और numpy जैसी बड़ी libraries भी इसी तरह सजी हैं — बस ज़्यादा files के साथ।

你用四个小文件构建了一个真实、可导入的包。用户只看到三个干净的函数名;_internal._clean() 被隐藏起来。textstats/ 内的文件用相对导入互相引用,所以包改名时无需修改它们。requests、numpy 这样的大库也是这样组织的 — 只是文件更多。


SectionSection第 13 节

The Standard Library — Modules You Already Have 📚Standard Library — Modules जो आपके पास पहले से हैं 📚标准库 — 你已经拥有的模块 📚

Python ships with hundreds of modules — "batteries included". Python itself can tell you how many:

Python सैकड़ों modules के साथ आता है — "batteries included"। Python ख़ुद बता सकता है कितने:

Python 自带数百个模块 —"自带电池"。Python 自己就能告诉你有多少:

📄 main.py
import sys

print(len(sys.stdlib_module_names), "standard library modules")
print("json" in sys.stdlib_module_names, "requests" in sys.stdlib_module_names)
print(sys.builtin_module_names[:6])
OUTPUT
290 standard library modules True False ('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_collections')
ModuleModule模块What it doesक्या करता है用途Exampleउदाहरण示例
osOperating-system tasksOperating system के काम操作系统相关os.getcwd()
sysInterpreter details, command-line argumentsInterpreter की जानकारी, command-line arguments解释器信息、命令行参数sys.argv[1]
pathlibModern file paths — prefer it to os.pathआधुनिक file paths — os.path से बेहतर现代文件路径 — 优先于 os.pathPath("data.csv").exists()
datetimeDates, times, time zonesतारीख़, समय, time zones日期、时间、时区datetime.now()
jsonRead and write JSONJSON पढ़ना और लिखना读写 JSONjson.loads(text)
csvRead and write CSV filesCSV files पढ़ना और लिखना读写 CSV 文件csv.reader(file)
reRegular expressionsRegular expressions正则表达式re.findall(r"\d+", text)
collectionsCounter, defaultdict, deque, namedtupleCounter(words)
itertoolsFast iterator building blocksतेज़ iterator औज़ार高效的迭代器工具chain, combinations
functoolsTools for functionsFunctions के लिए औज़ार函数工具reduce, lru_cache
randomRandom numbers and samplingRandom संख्याएँ और sampling随机数与抽样random.choice(items)
loggingProper logging (better than print)सही logging (print से बेहतर)规范日志(优于 print)logging.info("msg")
argparseCommand-line optionsCommand-line options命令行参数解析parser.add_argument()
unittestTesting frameworkTesting framework测试框架class T(TestCase):

SectionSection第 14 节

Third-Party Packages — pip, requirements.txt and venv 🌐Third-Party Packages — pip, requirements.txt और venv 🌐第三方包 — pip、requirements.txt 与 venv 🌐

Beyond the standard library, more than half a million projects live on PyPI, the Python Package Index. You install them with pip. Writing python -m pip (instead of plain pip) guarantees that pip installs into the same Python you are running.

Standard library के आगे, पाँच लाख से ज़्यादा projects PyPI (Python Package Index) पर हैं। आप इन्हें pip से install करते हैं। python -m pip लिखना (सिर्फ़ pip की जगह) पक्का करता है कि pip उसी Python में install करे जो आप चला रहे हैं।

在标准库之外,PyPI(Python 包索引)上有五十多万个项目,用 pip 安装。写 python -m pip(而不是单纯的 pip)可以确保 pip 安装到你正在运行的那个 Python 里。

# Install a package
$ python -m pip install requests

# Install an exact version
$ python -m pip install "pandas==2.2.3"

# Save the exact versions you use, then reinstall them anywhere
$ python -m pip freeze > requirements.txt
$ python -m pip install -r requirements.txt

# List or remove packages
$ python -m pip list
$ python -m pip uninstall requests

Virtual Environments — Essential for Real ProjectsVirtual Environments — असली Projects के लिए ज़रूरी虚拟环境 — 真实项目的必需品

# Create an isolated environment for THIS project only
$ python -m venv .venv

# Activate it (macOS / Linux)
$ source .venv/bin/activate

# Activate it (Windows, PowerShell or cmd)
> .venv\Scripts\activate

# Now pip installs ONLY into this project
(.venv) $ python -m pip install requests pandas

# Leave the environment
(.venv) $ deactivate
🚨
Don't Install into the System PythonSystem Python में Install न करें不要安装到系统 Python

Installing everything into one global Python leads to version fights: project A needs pandas 1.5, project B needs pandas 2.2, and only one can win. A virtual environment gives each project its own packages. Many Linux distributions and Homebrew Python now block global installs with an externally-managed-environment error (PEP 668) for exactly this reason — the fix is to create a venv, not to force it.

सब कुछ एक global Python में install करने से versions की लड़ाई होती है: project A को pandas 1.5 चाहिए, project B को pandas 2.2, और सिर्फ़ एक जीत सकता है। Virtual environment हर project को उसके अपने packages देता है। कई Linux distributions और Homebrew Python अब इसी वजह से global installs को externally-managed-environment error (PEP 668) से रोकते हैं — इलाज venv बनाना है, ज़बरदस्ती करना नहीं।

把所有东西装进同一个全局 Python 会导致版本冲突:项目 A 需要 pandas 1.5,项目 B 需要 pandas 2.2,只能有一个赢。虚拟环境为每个项目提供独立的包。正因如此,许多 Linux 发行版和 Homebrew 的 Python 现在会用 externally-managed-environment 错误(PEP 668)阻止 全局安装 — 正确做法是创建 venv,而不是强行安装。


SectionSection第 15 节

Common Pitfalls — and the Circular Import 🔄आम गड्ढे — और Circular Import 🔄常见陷阱 — 以及循环导入 🔄

🔄
Circular importsCircular imports循环导入
A imports B, B imports AA, B को import करे, B, A कोA 导入 B,B 导入 A
Each module waits for the other to finish. Symptom: ImportError: cannot import name …. Fix: move shared code into a third module, or move one import inside a function.
हर module दूसरे के पूरा होने का इंतज़ार करता है। लक्षण: ImportError: cannot import name …। इलाज: साझा code को तीसरे module में ले जाएँ, या एक import को function के अंदर ले जाएँ।
每个模块都在等对方完成。症状:ImportError: cannot import name …。修复:把共享代码移到第三个模块,或把其中一个导入挪进函数内部。
🚫
Shadowing the standard libraryStandard library को ढकना遮蔽标准库
a file called random.pyrandom.py नाम की file名为 random.py 的文件
Your local random.py is found before the real one, and everything that uses random breaks. Never name files after modules you use.
आपकी local random.py असली से पहले मिलती है, और random इस्तेमाल करने वाली हर चीज़ टूट जाती है। Files के नाम इस्तेमाल होने वाले modules पर कभी न रखें।
你本地的 random.py 先于真正的模块被找到,所有用到 random 的地方都会出错。永远不要用你所使用模块的名字给文件命名。
🏃
Running package files by pathPackage files को path से चलाना按路径运行包内文件
python pkg/mod.py
Imports that work everywhere else suddenly fail. Run modules inside a package with python -m pkg.mod from the project folder.
जो imports बाक़ी हर जगह चलते हैं, वे अचानक fail होते हैं। Package के अंदर के modules को project folder से python -m pkg.mod से चलाएँ।
在别处都正常的导入突然失败。在项目文件夹中用 python -m pkg.mod 运行包内的模块。

A Circular Import, Step by Stepएक Circular Import, कदम-दर-कदम循环导入,一步一步看

📄 shop/users.py
# shop/users.py
from shop.orders import Order

class User:
    pass
📄 shop/orders.py
# shop/orders.py
from shop.users import User        # <- circular!

class Order:
    pass
📄 main.py
import shop.users
OUTPUT
Traceback (most recent call last): File "/home/aarav/myproject/main.py", line 1, in <module> import shop.users File "/home/aarav/myproject/shop/users.py", line 2, in <module> from shop.orders import Order File "/home/aarav/myproject/shop/orders.py", line 2, in <module> from shop.users import User # <- circular! ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ImportError: cannot import name 'User' from partially initialized module 'shop.users' (most likely due to a circular import) (/home/aarav/myproject/shop/users.py)

Try It — Circular Import Tracerकरके देखें — Circular Import Tracer动手试试 — 循环导入跟踪器

Step through the broken version and the fixed one. Each step was recorded by Python's tracer while really running these files. Watch the sys.modules panel: a module is added the moment it starts loading — that half-loaded entry is the whole problem.

टूटा हुआ और ठीक किया हुआ version, दोनों कदम-दर-कदम चलाएँ। हर कदम इन files को सच में चलाते समय Python के tracer से दर्ज हुआ। sys.modules panel देखें: module तभी जुड़ जाता है जब वह load होना शुरू होता है — यही आधी-load हुई entry पूरी समस्या है।

逐步查看出错版本和修复版本。每一步都是在真实运行这些文件时由 Python 跟踪器记录的。注意 sys.modules 面板:模块在 开始 加载的那一刻就被加入 — 这个"加载了一半"的条目正是问题所在。

🔄 Circular Import TracerWhy "partially initialized"? Watch sys.modules."partially initialized" क्यों? sys.modules देखें।为什么是"部分初始化"?看看 sys.modules。

The Fix — Move Shared Code into a Third Moduleइलाज — साझा Code को तीसरे Module में ले जाएँ修复 — 把共享代码移到第三个模块

📄 models.py
# models.py - shared types live here
class User:
    def __init__(self, name):
        self.name = name

class Order:
    def __init__(self, user, item):
        self.user, self.item = user, item
📄 users.py
# users.py
from models import User

def make_user(name):
    return User(name)
📄 orders.py
# orders.py
from models import Order
from users import make_user

def place(name, item):
    return Order(make_user(name), item)
📄 main.py
from orders import place
o = place("Aarav", "notebook")
print(o.user.name, "ordered a", o.item)
OUTPUT
Aarav ordered a notebook
🔍
A Python 3.13 QuirkPython 3.13 की एक अजीब बातPython 3.13 的一个怪癖

When the two modules are loose files next to your script (not inside a package), Python 3.13 reports the same circular import with a misleading hint: "consider renaming … if it has the same name as a library you intended to import". The real cause is still the circle. Inside a package, as above, you get the clear message "partially initialized module … most likely due to a circular import".

जब दोनों modules आपकी script के बगल में खुली files हों (package के अंदर नहीं), तो Python 3.13 उसी circular import को भ्रामक संकेत के साथ बताता है: "consider renaming … if it has the same name as a library you intended to import"। असली वजह फिर भी circle ही है। Package के अंदर, जैसे ऊपर, आपको साफ़ संदेश मिलता है: "partially initialized module … most likely due to a circular import"।

当两个模块是脚本旁边的零散文件(不在包内)时,Python 3.13 报告同样的循环导入,却附带误导性提示:"consider renaming … if it has the same name as a library you intended to import"。真正的原因仍是循环。在包内(如上)你会得到清晰的信息:"partially initialized module … most likely due to a circular import"。


SectionSection第 16 节

Golden Rules and Quick Quiz 🏆सुनहरे नियम और छोटा Quiz 🏆黄金法则与小测验 🏆

🔑 Modules and Packages — Rules to RememberModules और Packages — याद रखने वाले नियम模块与包 — 必须记住的规则
1
Guard runnable code with if __name__ == "__main__":. Keep the top level of a module to definitions, so importing it has no side effects.
चलने वाले code को if __name__ == "__main__": से बचाएँ। Module के top level पर सिर्फ़ परिभाषाएँ रखें, ताकि import करने पर कोई side effect न हो।
用 if __name__ == "__main__": 保护可运行代码。模块顶层只放定义,让导入没有副作用。
2
Avoid from module import * outside quick REPL experiments. Explicit imports keep names traceable and prevent silent overwrites.
जल्दी के REPL प्रयोगों के बाहर from module import * से बचें। Explicit imports नामों का पता रखते हैं और चुपचाप overwrite होने से रोकते हैं।
除了在 REPL 中快速试验,避免 from module import *。显式导入让名字可追溯,防止悄悄覆盖。
3
Prefer absolute imports (PEP 8). Use relative imports for close neighbours inside the same package.
Absolute imports को तरजीह दें (PEP 8)। उसी package के क़रीबी पड़ोसियों के लिए relative imports इस्तेमाल करें।
优先使用绝对导入(PEP 8)。同一包内的近邻模块可用相对导入。
4
Run modules inside a package with python -m package.module, from the project folder — never by their file path.
Package के अंदर के modules को python -m package.module से चलाएँ, project folder से — कभी उनके file path से नहीं।
在项目文件夹中用 python -m package.module 运行包内模块 — 不要按文件路径运行。
5
Never name a file after a module you use — no random.py, json.py, email.py, test.py. Your folder is searched before the standard library.
किसी इस्तेमाल होने वाले module के नाम पर file का नाम कभी न रखें — random.py, json.py, email.py, test.py नहीं। आपका folder standard library से पहले खोजा जाता है।
永远不要用你所使用模块的名字命名文件 — 不要 random.py、json.py、email.py、test.py。你的文件夹在标准库之前被搜索。
6
Use a virtual environment for every project and record your packages in requirements.txt. Install with python -m pip.
हर project के लिए virtual environment इस्तेमाल करें और अपने packages requirements.txt में दर्ज करें। python -m pip से install करें।
每个项目都使用虚拟环境,并把包记录在 requirements.txt 中。用 python -m pip 安装。
7
Mark internal names with a leading underscore, and list the public ones in __all__. The underscore is a signal, not a lock.
अंदरूनी नामों को आगे के underscore से चिह्नित करें, और सार्वजनिक नामों को __all__ में लिखें। Underscore एक संकेत है, ताला नहीं।
用前导下划线标记内部名字,并在 __all__ 中列出公开名字。下划线是信号,不是锁。
8
Break import cycles by moving shared code into a third module. Moving an import inside a function also works, but treat it as a stopgap.
Import cycles तोड़ें — साझा code को तीसरे module में ले जाकर। Import को function के अंदर ले जाना भी चलता है, पर इसे अस्थायी इलाज मानें।
打破导入循环:把共享代码移到第三个模块。把导入挪进函数也可以,但只当作权宜之计。
9
Group imports at the top of the file: standard library, then third-party packages, then your own modules, with a blank line between groups (PEP 8).
Imports को file के सबसे ऊपर समूहों में रखें: standard library, फिर third-party packages, फिर आपके अपने modules, समूहों के बीच ख़ाली line (PEP 8)।
在文件顶部分组导入:先标准库,再第三方包,最后你自己的模块,组之间空一行(PEP 8)。

Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题

🏆 QuizPick an answer to see why.जवाब चुनें और कारण देखें।选一个答案,看看原因。
🎯
Key Takeawayमुख्य सीख关键要点

A module is a file, a package is a folder, and import finds them by asking a fixed list of places — first match wins, and the result is cached in sys.modules. Guard script code with __name__, run package modules with -m, never shadow a library name, break import cycles with a shared module, and give every project its own virtual environment.

Module एक file है, package एक folder, और import उन्हें जगहों की एक तय सूची से पूछकर ढूँढता है — पहला match जीतता है, और नतीजा sys.modules में cache होता है। Script code को __name__ से बचाएँ, package modules को -m से चलाएँ, किसी library का नाम कभी न ढकें, import cycles को साझा module से तोड़ें, और हर project को उसका अपना virtual environment दें।

模块是一个文件,包是一个文件夹,import 通过询问一组固定的位置找到它们 — 第一个匹配获胜,结果缓存在 sys.modules 中。用 __name__ 保护脚本代码,用 -m 运行包内模块,永远不要遮蔽库名,用共享模块打破导入循环,并为每个项目准备独立的虚拟环境。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.