BASIC Python (Beginner Level) 📂 Basic Python Programming · 15 of 15 241 min read

Python Exception Handling

A visual, story-driven Python exception guide: the full class hierarchy from BaseException down, try / except / else / finally flow, why order matters, raising, chaining with raise from e, custom exceptions, and — critically — how to see the real error using the traceback module and logger.exception(). Learn to read tracebacks bottom-up so debugging stops being guesswork.

🌐
SectionSection第 01 节

Why Exceptions Exist — The Fire Alarm in Your Program 🚨Exceptions क्यों हैं — आपके Program का Fire Alarm 🚨为什么要有异常 — 程序里的火警 🚨

The Kitchen with No Alarmsबिना Alarm वाली रसोई没有警报的厨房
Imagine a restaurant kitchen where problems are never reported. The stove catches fire and the chef keeps chopping onions. A knife falls and the sous-chef keeps plating. Every disaster is discovered by the customer, at the table, with the food.

That is a program without exception handling. Errors happen — the file isn't there, the network drops, the user typed "hello" where a number belonged — and the program either crashes or quietly produces nonsense that someone discovers three screens later.

Exceptions are the fire alarms of your code. When something goes wrong, an alarm rings, the current work stops at once, and the alarm travels up through the functions that called each other, looking for someone ready to deal with it. Your job is to decide where the alarms are answered — and how.
सोचिए एक restaurant की रसोई जहाँ समस्याओं की कभी ख़बर नहीं दी जाती। चूल्हे में आग लगती है और chef प्याज़ काटता रहता है। चाकू गिरता है और सहायक plate सजाता रहता है। हर गड़बड़ी का पता ग्राहक को चलता है, मेज़ पर, खाने के साथ।

Exception handling के बिना program ऐसा ही होता है। Errors होते हैं — file नहीं मिली, network टूट गया, user ने संख्या की जगह "hello" लिख दिया — और program या तो crash हो जाता है या चुपचाप बकवास नतीजे देता है जिनका पता किसी को तीन screen बाद चलता है।

Exceptions आपके code के fire alarm हैं। जब कुछ ग़लत होता है, alarm बजता है, चल रहा काम तुरंत रुकता है, और alarm उन functions में ऊपर की ओर जाता है जिन्होंने एक-दूसरे को बुलाया था, ऐसे किसी को ढूँढते हुए जो इसे संभाल सके। आपका काम तय करना है कि alarm का जवाब कहाँ और कैसे दिया जाए।
想象一个餐厅厨房,出了问题从来没人报告。炉子着火了,主厨还在切洋葱。刀掉了,副厨还在摆盘。每一个灾难都是顾客在餐桌上、在食物里发现的。

没有异常处理的程序就是这样。错误总会发生 — 文件不在、网络断了、用户在该填数字的地方输入了 "hello" — 程序要么崩溃,要么悄悄给出荒谬的结果,直到三个页面之后才被人发现。

异常 就是代码里的火警。出问题时,警报响起,当前工作立刻停止,警报沿着互相调用的函数 向上 传递,寻找能处理它的人。你的任务是决定在哪里应答警报 — 以及如何应答。

In Python, an exception is an object that describes an error. When something goes wrong, Python raises an exception. If no code catches it, the program stops and prints a traceback. Exception handling is how you say: "I know this can fail — here is what should happen when it does."

Python में exception एक object है जो किसी error का वर्णन करता है। जब कुछ ग़लत होता है, Python एक exception raise करता है। अगर कोई code उसे नहीं पकड़ता, तो program रुक जाता है और एक traceback छापता है। Exception handling यह कहने का तरीका है: "मुझे पता है यह fail हो सकता है — fail होने पर यह होना चाहिए।"

在 Python 中,异常 是一个描述错误的对象。出问题时,Python 会 抛出(raise) 一个异常。如果没有代码捕获它,程序就会停止并打印 traceback。异常处理就是在说:"我知道这里可能失败 — 失败时应该这样做。"

An Exception Travels Up the Call StackException Call Stack में ऊपर जाता है异常沿调用栈向上传递
An exception bubbles up the call stack<module>total(order)price_of(item)call stack — newest on topcall stack — सबसे नया ऊपर调用栈 — 最新的在上面🔥KeyError raised hereKeyError यहाँ raise हुआKeyError 在这里抛出bubbles up until anऊपर तक जाता है जब तक一路向上冒,直到except catches itकोई except पकड़ न ले被某个 except 捕获🛡️ try / except

price_of() fails. Python stops it and passes the exception to the function that called it, then to that function's caller, until a matching except is found — or the program stops.

price_of() fail होता है। Python उसे रोककर exception उसे बुलाने वाले function को देता है, फिर उसके caller को, जब तक match करता except न मिले — या program रुक न जाए।

price_of() 出错了。Python 停止它,把异常交给调用它的函数,再交给那个函数的调用者,直到找到匹配的 except — 或者程序停止。

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

Exceptions separate the happy path — what happens when everything works — from the recovery paths — what to do when things break. Without them, every function would drown in if error: return -1 checks. With them, the main logic stays clean and error handling lives in its own blocks.

Exceptions happy path — जब सब ठीक चले — को recovery paths — जब चीज़ें टूटें तब क्या करना है — से अलग करते हैं। इनके बिना हर function if error: return -1 जाँचों में डूब जाता। इनके साथ मुख्य logic साफ़ रहता है और error handling अपने अलग blocks में रहता है।

异常把 正常路径(一切顺利时发生的事)和 恢复路径(出问题时该做的事)分开。没有异常,每个函数都会淹没在 if error: return -1 的检查中。有了异常,主逻辑保持干净,错误处理放在专门的块里。

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

Look for the interactive boxes: the Hierarchy Explorer (Section 02), the Propagation Simulator (04), the Flow Stepper for try/except/else/finally (07) and the Traceback Reader (10). Every output they show was checked against real Python.

Interactive डिब्बे खोजें: Hierarchy Explorer (Section 02), Propagation Simulator (04), try/except/else/finally का Flow Stepper (07) और Traceback Reader (10)। इनका हर output असली Python से जाँचा गया है।

请留意这些互动框:Hierarchy Explorer(第 02 节)、Propagation Simulator(04)、try/except/else/finally 的 Flow Stepper(07)和 Traceback Reader(10)。它们显示的每个输出都与真实 Python 核对过。


SectionSection第 02 节

The Exception Hierarchy — A Family Tree of Errors 🌳Exception Hierarchy — Errors का Family Tree 🌳异常层次结构 — 错误的家族树 🌳

Every exception in Python is an object, and every exception class comes from one common ancestor: BaseException. This tree is not trivia. It is the tool that lets one except clause catch a whole group of related errors.

Python में हर exception एक object है, और हर exception class एक ही पूर्वज से आती है: BaseException। यह tree सिर्फ़ जानकारी नहीं है। यही वह औज़ार है जिससे एक except clause संबंधित errors का पूरा समूह पकड़ लेता है।

Python 中每个异常都是对象,每个异常类都来自同一个祖先:BaseException。这棵树不是冷知识,它是让一个 except 子句就能捕获一整组相关错误的工具。

BaseException
 ├── SystemExit                    # raised by sys.exit()
 ├── KeyboardInterrupt             # Ctrl+C
 ├── GeneratorExit                 # generator .close()
 ├── BaseExceptionGroup            # several errors at once (3.11+)
 └── Exception                     # <-- catch things HERE and below
      ├── ArithmeticError
      │    ├── ZeroDivisionError
      │    ├── OverflowError
      │    └── FloatingPointError
      ├── LookupError
      │    ├── IndexError          # my_list[999]
      │    └── KeyError            # my_dict['missing']
      ├── OSError                  # file/OS problems
      │    ├── FileNotFoundError
      │    ├── PermissionError
      │    ├── IsADirectoryError
      │    ├── ConnectionError
      │    │    ├── ConnectionRefusedError
      │    │    ├── ConnectionResetError
      │    │    └── ConnectionAbortedError
      │    └── TimeoutError
      ├── ValueError               # int('abc')
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         └── UnicodeEncodeError
      ├── TypeError                # wrong type
      ├── AttributeError           # obj.missing_attr
      ├── NameError                # undefined variable
      │    └── UnboundLocalError
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── RuntimeError
      │    ├── RecursionError
      │    └── NotImplementedError
      ├── StopIteration            # end of an iterator
      ├── AssertionError           # failed assert
      └── ExceptionGroup           # also a BaseExceptionGroup (3.11+)

This is a selection — Python has about 70 built-in exceptions. See them all with help("builtins") or the "Exception hierarchy" page of the Python docs.

यह एक चुनाव है — Python में लगभग 70 built-in exceptions हैं। सभी देखने के लिए help("builtins") या Python docs का "Exception hierarchy" page देखें।

这里只是一部分 — Python 有大约 70 个内置异常。可以用 help("builtins") 或 Python 文档的 "Exception hierarchy" 页面查看全部。

Try It — Hierarchy Explorerकरके देखें — Hierarchy Explorer动手试试 — 层次结构探索器

Pick the exception that is raised (click any name in the tree) and the class your except names. Python catches it only if the except class is on the raised exception's family line — the orange path up to the root.

जो exception raise होता है उसे चुनें (tree में किसी भी नाम पर click करें) और वह class चुनें जो आपका except लिखता है। Python उसे तभी पकड़ता है जब except वाली class raise हुए exception की परिवार line पर हो — root तक जाने वाला नारंगी रास्ता।

选择被 抛出 的异常(点击树中任意名字),再选择你的 except 写的类。只有当 except 的类位于被抛出异常的家族链上 — 也就是通往根的橙色路径 — Python 才会捕获它。

🌳 Hierarchy ExplorerDoes this except catch that exception? The family tree decides.क्या यह except उस exception को पकड़ेगा? Family tree तय करता है।这个 except 能捕获那个异常吗?家族树说了算。
Try:आज़माएँ:试试:
raisedraise हुआ被抛出its family lineउसकी परिवार line它的家族链except — caughtexcept — पकड़ाexcept — 捕获except — missedexcept — चूकाexcept — 未命中

SectionSection第 03 节

The Classes That Matter Most — What Each One Catches 🎯सबसे ज़रूरी Classes — हर एक क्या पकड़ती है 🎯最重要的几个类 — 各自捕获什么 🎯

There are dozens of built-in exceptions, but you will name only a few of them in your except clauses. Here they are, from the root down.

Built-in exceptions दर्जनों हैं, पर अपने except clauses में आप उनमें से कुछ ही लिखेंगे। ये रहीं, root से नीचे की ओर।

内置异常有几十个,但你在 except 子句里只会写其中几个。下面从根往下逐个介绍。

💀
BaseException
the root — never catchroot — कभी न पकड़ें根 — 永远不要捕获
The ancestor of everything. Catching it also catches KeyboardInterrupt and SystemExit, so Ctrl+C and sys.exit() stop working. Leave it to framework internals.
हर चीज़ का पूर्वज। इसे पकड़ने से KeyboardInterrupt और SystemExit भी पकड़े जाते हैं, इसलिए Ctrl+C और sys.exit() काम करना बंद कर देते हैं। इसे frameworks के अंदरूनी हिस्सों पर छोड़ दें।
一切的祖先。捕获它也会捕获 KeyboardInterrupt 和 SystemExit,导致 Ctrl+C 和 sys.exit() 失效。把它留给框架内部使用。
🛡️
Exception
the broadest safe catchसबसे बड़ा सुरक्षित catch最宽的安全捕获
The base of all "normal" runtime errors. If you must catch a broad category — say, at the top of a server loop — catch this, not BaseException.
सभी "सामान्य" runtime errors का आधार। अगर बड़ा समूह पकड़ना ज़रूरी हो — जैसे server loop के सबसे ऊपर — तो इसे पकड़ें, BaseException को नहीं।
所有"普通"运行时错误的基类。如果必须捕获一大类 — 比如在服务器循环的最外层 — 就捕获它,而不是 BaseException。
💽
OSError
files, sockets, processesfiles, sockets, processes文件、套接字、进程
Outside-world failures: file not found, permission denied, connection refused, timeout, disk full. FileNotFoundError, PermissionError and ConnectionError all inherit from it.
बाहरी दुनिया की गड़बड़ियाँ: file नहीं मिली, permission नहीं, connection refused, timeout, disk भरी। FileNotFoundError, PermissionError और ConnectionError सभी इससे inherit करते हैं।
外部世界的失败:文件找不到、权限被拒、连接被拒绝、超时、磁盘已满。FileNotFoundError、PermissionError 和 ConnectionError 都继承自它。
🎯
Specific classesख़ास classes具体的类
precise handlingसटीक handling精确处理
ValueError, KeyError, TypeError, ZeroDivisionError… Catch these when you know exactly what can go wrong and how to recover. The narrower the catch, the safer the code.
ValueError, KeyError, TypeError, ZeroDivisionError… इन्हें तब पकड़ें जब आपको ठीक-ठीक पता हो कि क्या ग़लत हो सकता है और कैसे संभलना है। Catch जितना संकरा, code उतना सुरक्षित।
ValueError、KeyError、TypeError、ZeroDivisionError…… 当你确切知道会出什么错、如何恢复时,就捕获它们。捕获范围越窄,代码越安全。
👋
KeyboardInterrupt
Ctrl+C
Inherits from BaseException, not Exception. That is deliberate: except Exception: will not swallow it, so users can always stop your program with Ctrl+C.
Exception से नहीं, BaseException से inherit करता है। यह जान-बूझकर है: except Exception: इसे नहीं निगलता, इसलिए user हमेशा Ctrl+C से आपका program रोक सकते हैं।
继承自 BaseException 而不是 Exception。这是有意的:except Exception: 不会吞掉它,所以用户总能用 Ctrl+C 停止你的程序。
🚪
SystemExit
sys.exit()
Raised when code calls sys.exit(). It also sits directly under BaseException, so a stray except Exception: cannot block a clean shutdown.
जब code sys.exit() बुलाता है तब raise होता है। यह भी सीधे BaseException के नीचे है, इसलिए कोई भटका हुआ except Exception: साफ़ shutdown नहीं रोक सकता।
当代码调用 sys.exit() 时抛出。它也直接位于 BaseException 之下,所以一个随手写的 except Exception: 挡不住正常退出。
⚠️
Why KeyboardInterrupt Is Not an ExceptionKeyboardInterrupt एक Exception क्यों नहीं है为什么 KeyboardInterrupt 不是 Exception

This is one of Python's smartest design choices. If KeyboardInterrupt were an Exception, every except Exception: in the world would swallow Ctrl+C, and users would need Task Manager to stop a runaway script. Placing it directly under BaseException means broad handlers still let the user out. (A bare except: does not — it catches BaseException.)

यह Python के सबसे समझदार design फ़ैसलों में से एक है। अगर KeyboardInterrupt एक Exception होता, तो दुनिया का हर except Exception: Ctrl+C निगल जाता, और बेक़ाबू script रोकने के लिए users को Task Manager खोलना पड़ता। इसे सीधे BaseException के नीचे रखने से बड़े handlers भी user को बाहर जाने देते हैं। (खाली except: नहीं जाने देता — वह BaseException पकड़ता है।)

这是 Python 最聪明的设计之一。如果 KeyboardInterrupt 是 Exception,世界上每个 except Exception: 都会吞掉 Ctrl+C,用户就得打开任务管理器才能停止失控的脚本。把它直接放在 BaseException 之下,宽泛的处理器也会让用户退出。(裸 except: 不会 — 它捕获的是 BaseException。)


SectionSection第 04 节

The try / except Statement — Basic Anatomy 🧪try / except Statement — बुनियादी बनावट 🧪try / except 语句 — 基本结构 🧪

The simplest handler has two parts: a try block that holds the risky code, and an except block that runs only if a matching exception is raised inside try.

सबसे सरल handler के दो हिस्से हैं: एक try block जिसमें जोखिम वाला code है, और एक except block जो तभी चलता है जब try के अंदर match करता exception raise हो।

最简单的处理器有两部分:放风险代码的 try 块,以及只有当 try 内抛出匹配异常时才运行的 except 块。

for text in ["25", "twenty"]:          # pretend the user typed these
    try:
        # The "maybe this will fail" code
        age = int(text)
        print(f"You are {age} years old.")
    except ValueError:
        # Runs only if a ValueError was raised inside try
        print(f"{text!r} is not a valid number.")
OUTPUT
You are 25 years old. 'twenty' is not a valid number.

The loop feeds two pretend answers instead of calling input(), so you can see both paths in one run.

Loop input() बुलाने की जगह दो नक़ली जवाब देता है, ताकि एक ही run में दोनों रास्ते दिखें।

这个循环用两个假装的输入代替 input(),让你一次运行就能看到两条路径。

What Actually Happensअसल में क्या होता है实际发生了什么

01
Python enters the try blockPython try block में जाता हैPython 进入 try 块
Code runs line by line, exactly as normal.
Code सामान्य रूप से line-by-line चलता है।
代码像平常一样逐行运行。
02
Was an exception raised?क्या exception raise हुआ?抛出异常了吗?
If not, Python skips every except block and moves past the whole statement.
अगर नहीं, तो Python हर except block छोड़कर पूरे statement के आगे चला जाता है।
如果没有,Python 跳过所有 except 块,越过整个语句。
03
If yes — check each except in orderअगर हाँ — हर except को क्रम से जाँचें如果有 — 按顺序检查每个 except
The rest of try is skipped. Python takes the first except whose class matches — subclasses match too (it uses isinstance).
try का बाक़ी हिस्सा छूट जाता है। Python पहला except चुनता है जिसकी class match करे — subclasses भी match करती हैं (यह isinstance इस्तेमाल करता है)।
try 剩余部分被跳过。Python 选择第一个类匹配的 except — 子类也算匹配(它用的是 isinstance)。
04
The handler runs — the exception is "handled"Handler चलता है — exception "handled" हो गया处理器运行 — 异常"已处理"
Execution continues after the whole try/except statement. Nothing is re-raised unless you do it yourself.
Execution पूरे try/except statement के बाद आगे चलता है। जब तक आप ख़ुद न करें, कुछ फिर से raise नहीं होता।
执行从整个 try/except 语句之后继续。除非你自己再次抛出,否则不会重新抛出。
05
If no except matchesअगर कोई except match न करे如果没有 except 匹配
The exception keeps travelling up the call stack until something catches it — or the program dies with a traceback. Watch it happen below.
Exception call stack में ऊपर जाता रहता है जब तक कोई उसे पकड़ न ले — या program traceback के साथ रुक न जाए। नीचे इसे होते देखें।
异常沿调用栈继续向上,直到有人捕获它 — 或程序带着 traceback 终止。下面亲眼看看。
📈
Catching a Class Also Catches Its SubclassesClass पकड़ने से उसकी Subclasses भी पकड़ी जाती हैं捕获一个类也会捕获它的子类

except OSError: matches FileNotFoundError, PermissionError, ConnectionRefusedError and every other OSError descendant. This is why the hierarchy matters — one line covers dozens of related errors.

except OSError:, FileNotFoundError, PermissionError, ConnectionRefusedError और OSError के हर वंशज से match करता है। इसीलिए hierarchy मायने रखती है — एक line दर्जनों संबंधित errors संभाल लेती है।

except OSError: 能匹配 FileNotFoundError、PermissionError、ConnectionRefusedError 以及所有 OSError 的后代。这就是层次结构重要的原因 — 一行就能覆盖几十种相关错误。

Try It — Propagation Simulatorकरके देखें — Propagation Simulator动手试试 — 传播模拟器

Three functions call each other. The second item in the order is broken, so price_of() raises an exception. Choose where to put a try/except, which class it catches, and which bug happens. Then step through and watch the 🔥 bubble up the call stack. The output at the end is from real Python.

तीन functions एक-दूसरे को बुलाते हैं। Order का दूसरा item टूटा है, इसलिए price_of() exception raise करता है। चुनें कि try/except कहाँ रखना है, वह कौन-सी class पकड़ता है, और कौन-सा bug होता है। फिर कदम-दर-कदम चलें और 🔥 को call stack में ऊपर जाते देखें। आख़िर का output असली Python का है।

三个函数互相调用。订单的第二个商品有问题,所以 price_of() 抛出异常。选择把 try/except 放在 哪里、它捕获 哪个类、发生 哪种 bug。然后逐步执行,看 🔥 沿调用栈向上冒。最后的输出来自真实 Python。

🫧 Propagation SimulatorWhere is the alarm answered? Move the try/except and see.Alarm का जवाब कहाँ दिया जाता है? try/except हिलाकर देखें।警报在哪里被应答?移动 try/except 看看。
try/except inकहाँ位置
except
bug
🔍
Three Things to Noticeतीन बातें ध्यान दें注意三点

(1) The handler can be in any function above the error — the exception finds it. (2) Once an exception leaves a function, the rest of that function never runs. (3) Choose ValueError and the handler is checked but skipped: a KeyError is not a ValueError, so the program still crashes.

(1) Handler error के ऊपर के किसी भी function में हो सकता है — exception उसे ढूँढ लेता है। (2) एक बार exception किसी function से निकल जाए, तो उस function का बाक़ी हिस्सा कभी नहीं चलता। (3) ValueError चुनें तो handler जाँचा जाता है पर छोड़ दिया जाता है: KeyError एक ValueError नहीं है, इसलिए program फिर भी crash होता है।

(1) 处理器可以位于错误之上的 任何 函数中 — 异常会找到它。(2) 异常一旦离开某个函数,该函数剩余部分就永远不会运行。(3) 选择 ValueError 时,处理器会被检查但被跳过:KeyError 不是 ValueError,所以程序仍然崩溃。


SectionSection第 05 节

Multiple except Blocks — Different Handling for Different Errors 🔀कई except Blocks — अलग Errors के लिए अलग Handling 🔀多个 except 块 — 不同错误不同处理 🔀

One try can be followed by many except blocks. Python checks them top to bottom and runs the first one that matches — like an if / elif chain for errors.

एक try के बाद कई except blocks हो सकते हैं। Python उन्हें ऊपर से नीचे जाँचता है और पहला match करने वाला चलाता है — errors के लिए if / elif chain जैसा।

一个 try 后面可以跟多个 except 块。Python 从上到下检查,运行 第一个 匹配的 — 就像错误版的 if / elif 链。

def get_user(users, index):
    try:
        user = users[index]
        return user["name"].upper()
    except IndexError:
        return "No user at that position"
    except KeyError:
        return "User has no name field"
    except AttributeError:
        return "Name is not a string"

users = [
    {"name": "Aarav"},
    {"email": "p@ex.com"},        # missing 'name'
    {"name": 42},                 # not a string
]

print(get_user(users, 0))         # happy path
print(get_user(users, 1))         # KeyError
print(get_user(users, 2))         # AttributeError
print(get_user(users, 99))        # IndexError
OUTPUT
AARAV User has no name field Name is not a string No user at that position

Catching Several Types in One Blockएक Block में कई Types पकड़ना一个块捕获多种类型

If several exceptions deserve the same treatment, list them in a tuple. as e gives you the exception object so you can print its type and message:

अगर कई exceptions को एक जैसा इलाज चाहिए, तो उन्हें tuple में लिखें। as e आपको exception object देता है ताकि आप उसका type और संदेश print कर सकें:

如果几种异常需要同样的处理,就把它们写成元组。as e 给你异常对象,可以打印它的类型和信息:

def parse_price(record):
    try:
        return float(record["price"]) * record["qty"]
    except (ValueError, TypeError, KeyError) as e:
        print(f"Input problem -> {type(e).__name__}: {e}")
        return 0.0

print(parse_price({"price": "9.5", "qty": 2}))
print(parse_price({"price": "abc", "qty": 2}))     # ValueError
print(parse_price({"price": None, "qty": 2}))      # TypeError
print(parse_price({"qty": 2}))                     # KeyError
OUTPUT
19.0 Input problem -> ValueError: could not convert string to float: 'abc' 0.0 Input problem -> TypeError: float() argument must be a string or a real number, not 'NoneType' 0.0 Input problem -> KeyError: 'price' 0.0
💡
The as e Clauseas e Clauseas e 子句

except SomeError as e: binds the exception object to e inside the block. You can read e.args, log str(e) or check type(e).__name__. Without as, you handle the error but lose its details. Note: Python deletes e when the block ends, so save it to another name if you need it later.

except SomeError as e: block के अंदर exception object को e से जोड़ता है। आप e.args पढ़ सकते हैं, str(e) log कर सकते हैं या type(e).__name__ देख सकते हैं। as के बिना आप error संभालते हैं पर उसकी जानकारी खो देते हैं। ध्यान दें: block ख़त्म होते ही Python e मिटा देता है, इसलिए बाद में चाहिए तो उसे किसी दूसरे नाम में रखें।

except SomeError as e: 在块内把异常对象绑定到 e。你可以读取 e.args、记录 str(e) 或查看 type(e).__name__。不写 as,你处理了错误却丢掉了细节。注意:块结束时 Python 会删除 e,如果之后还要用,请把它存到另一个名字。

🆕
Python 3.14: Brackets Now Optional (Without as)Python 3.14: Brackets अब ज़रूरी नहीं (as के बिना)Python 3.14:括号可省略(不带 as 时)

Since Python 3.14 (PEP 758) you may write except ValueError, TypeError: without brackets. If you use as e, the brackets are still required: except (ValueError, TypeError) as e:. The bracket form works on every version, so it is the safe choice for shared code.

Python 3.14 (PEP 758) से आप brackets के बिना except ValueError, TypeError: लिख सकते हैं। अगर as e इस्तेमाल करें, तो brackets अब भी ज़रूरी हैं: except (ValueError, TypeError) as e:। Bracket वाला रूप हर version पर चलता है, इसलिए साझा code के लिए यही सुरक्षित है।

从 Python 3.14(PEP 758)起,可以不加括号写 except ValueError, TypeError:。如果用了 as e,仍然必须加括号:except (ValueError, TypeError) as e:。带括号的写法在所有版本都能用,所以共享代码用它更稳妥。


SectionSection第 06 节

Order Matters — Specific Before General 🪜क्रम मायने रखता है — ख़ास पहले, आम बाद में 🪜顺序很重要 — 先具体后宽泛 🪜

The Overeager Bouncerज़्यादा जोशीला Bouncer过于积极的保安
A nightclub has two bouncers at the door. The first is told: "Stop anyone under 25." The second is told: "Check the ID of anyone under 21 and call their parents." A 19-year-old walks up. The first bouncer stops them and sends them away — the second bouncer never gets to do their job. The specialist stood behind the generalist, so the specialist was never needed.

That is exactly what happens when a broad except comes before a narrow one. Python takes the first match and never checks the rest. Python does not even warn you.
एक nightclub के दरवाज़े पर दो bouncers हैं। पहले को कहा गया है: "25 से कम उम्र वाले हर किसी को रोको।" दूसरे को कहा गया है: "21 से कम वालों की ID जाँचो और उनके माता-पिता को फ़ोन करो।" एक 19 साल का युवक आता है। पहला bouncer उसे रोककर लौटा देता है — दूसरे को अपना काम करने का मौक़ा ही नहीं मिलता। विशेषज्ञ आम bouncer के पीछे खड़ा था, इसलिए उसकी कभी ज़रूरत ही नहीं पड़ी।

जब बड़ा except छोटे वाले से पहले आता है, ठीक यही होता है। Python पहला match लेता है और बाक़ी को कभी नहीं जाँचता। Python चेतावनी भी नहीं देता।
夜店门口有两个保安。第一个接到的指令是:"拦下所有 25 岁以下的人。"第二个接到的指令是:"检查 21 岁以下的人的证件并联系他们的父母。"一个 19 岁的年轻人走来。第一个保安拦下他并把他打发走 — 第二个保安根本没机会履行职责。专员站在了通才的 后面,所以永远轮不到专员。

把宽泛的 except 放在具体的之前,发生的正是这种事。Python 选第一个匹配的,从不检查其余的。Python 甚至不会警告你。
🚫 Wrong — General Before Specificग़लत — आम पहले, ख़ास बाद में错误 — 宽泛在前,具体在后
CodeCode代码What happensक्या होता है会发生什么
except OSErrorCatches everything I/O relatedहर I/O वाली चीज़ पकड़ता है捕获所有 I/O 相关错误
except FileNotFoundErrorDead code — never reachedDead code — कभी नहीं पहुँचता死代码 — 永远到不了
Resultनतीजा结果Missing files cannot be handled on their ownग़ायब files को अलग से नहीं संभाल सकते无法单独处理文件缺失
✅ Right — Specific Before Generalसही — ख़ास पहले, आम बाद में正确 — 具体在前,宽泛在后
CodeCode代码What happensक्या होता है会发生什么
except FileNotFoundErrorRuns when the file is missingFile ग़ायब होने पर चलता है文件缺失时运行
except OSErrorRuns for other I/O failuresबाक़ी I/O गड़बड़ियों पर चलता है其他 I/O 失败时运行
Resultनतीजा结果Each error gets its own responseहर error को अपना जवाब मिलता है每种错误都有自己的应对
# The correct ordering: narrow to broad
try:
    with open("data.txt", encoding="utf-8") as f:
        content = f.read()
except FileNotFoundError:
    print("File does not exist - starting with empty content")
    content = ""
except PermissionError:
    print("No permission - ask the user for another location")
    raise
except OSError as e:
    print(f"Some other I/O problem: {e}")
    raise
print(repr(content))
OUTPUT
File does not exist - starting with empty content ''

SectionSection第 07 节

The Full Statement — try / except / else / finally 🧩पूरा Statement — try / except / else / finally 🧩完整语句 — try / except / else / finally 🧩

Python's full exception statement has four clauses. Most code uses only try and except, but else and finally each solve a real problem.

Python के पूरे exception statement में चार clauses हैं। ज़्यादातर code सिर्फ़ try और except इस्तेमाल करता है, पर else और finally दोनों एक असली समस्या हल करते हैं।

Python 完整的异常语句有四个子句。大多数代码只用 try 和 except,但 else 和 finally 各自解决一个实际问题。

🔐 The Four Clauses and When Each Runsचार Clauses और हर एक कब चलता है四个子句及各自何时运行
try
The risky code you want to guard. Runs top to bottom until an exception is raised or the block ends. जोखिम वाला code जिसे आप बचाना चाहते हैं। ऊपर से नीचे चलता है जब तक exception raise न हो या block ख़त्म न हो। 你想保护的风险代码。从上到下运行,直到抛出异常或块结束。
except
Runs only if a matching exception was raised inside try. You can have several. तभी चलता है जब try के अंदर match करता exception raise हुआ हो। कई हो सकते हैं। 只有当 try 内抛出匹配的异常时才运行。可以有多个。
else
Runs only if try finished without raising anything. For code that should run on success but is not itself risky. तभी चलता है जब try बिना कुछ raise किए ख़त्म हो। उस code के लिए जो सफलता पर चलना चाहिए पर ख़ुद जोखिम वाला नहीं है। 只有当 try 没有 抛出任何异常地完成时才运行。用于成功时才执行、本身没有风险的代码。
finally
Runs always — on success, on failure, after return, even when an exception is still flying. Used for cleanup: closing connections, releasing locks. हमेशा चलता है — सफलता पर, विफलता पर, return के बाद, तब भी जब exception अभी उड़ रहा हो। Cleanup के लिए: connections बंद करना, locks छोड़ना। 总是 运行 — 成功时、失败时、return 之后,甚至异常还在飞行时。用于清理:关闭连接、释放锁。
Execution FlowExecution का बहाव执行流程
try except else finally flowtryno exceptionकोई exception नहीं无异常exception raisedexception raise हुआ抛出异常elseexcept match?yes → run itहाँ → चलाओ是 → 运行它no → keep flyingनहीं → उड़ता रहे否 → 继续飞finally← ALWAYS← हमेशा← 总是continue after the block (or the exception carries on upward)block के बाद आगे चलो (या exception ऊपर जाता रहे)在块之后继续(或异常继续向上传)

Success goes through else; a matching error goes through except; both meet at finally. An unmatched error also passes through finally before it leaves.

सफलता else से जाती है; match करता error except से; दोनों finally पर मिलते हैं। Match न होने वाला error भी जाने से पहले finally से गुज़रता है।

成功走 else;匹配的错误走 except;两者在 finally 汇合。未匹配的错误离开前也会经过 finally。

All Four Clauses in Actionचारों Clauses काम करते हुए四个子句的实际运行

def read_config(path):
    f = None
    try:
        print("1. try: opening", path)
        f = open(path, encoding="utf-8")
        content = f.read()
    except FileNotFoundError:
        print("2. except: file missing")
        return None
    else:
        print("2. else: parsing content")
        return content.strip().splitlines()
    finally:
        print("3. finally: cleanup")
        if f is not None:
            f.close()

print("--- success case ---")
print(read_config("settings.txt"))

print("--- failure case ---")
print(read_config("missing.txt"))
OUTPUT
--- success case --- 1. try: opening settings.txt 2. else: parsing content 3. finally: cleanup ['debug = true', 'theme = dark', 'language = en'] --- failure case --- 1. try: opening missing.txt 2. except: file missing 3. finally: cleanup None

Try It — Flow Stepperकरके देखें — Flow Stepper动手试试 — 流程步进器

Type any value for text (or tap a chip) and step through. Watch which clauses light up, which are skipped, and how finally runs every single time — even when the error is not caught.

text के लिए कोई भी value लिखें (या chip दबाएँ) और कदम-दर-कदम चलें। देखें कौन-से clauses जलते हैं, कौन-से छूटते हैं, और finally कैसे हर बार चलता है — तब भी जब error पकड़ा न जाए।

为 text 输入任意值(或点一个按钮),然后逐步执行。观察哪些子句亮起、哪些被跳过,以及 finally 如何每一次都运行 — 即使错误没有被捕获。

🚦 Flow Steppertry → except / else → finally. Same code, different input.try → except / else → finally। वही code, अलग input।try → except / else → finally。同样的代码,不同的输入。
Try:आज़माएँ:试试:
🔑
Why Not Put the Success Code in try?सफलता वाला Code try में क्यों नहीं?为什么不把成功代码放进 try?

You could put the parsing inside try, but then a bug in the parsing could be caught by a handler meant for something else. else makes the intent clear: "only these lines are risky — the rest runs only if they succeeded, and its own errors should surface normally."

आप parsing को try के अंदर रख सकते हैं, पर तब parsing का कोई bug किसी और चीज़ के लिए बने handler से पकड़ा जा सकता है। else इरादा साफ़ करता है: "सिर्फ़ ये lines जोखिम वाली हैं — बाक़ी तभी चलेगा जब ये सफल हों, और उसके अपने errors सामान्य रूप से सामने आने चाहिए।"

你可以把解析放进 try,但那样解析中的 bug 可能被本为别的事准备的处理器捕获。else 让意图一目了然:"只有这几行有风险 — 其余代码只在它们成功后运行,它自己的错误应该正常暴露出来。"

A Trap: return Inside finallyएक जाल: finally के अंदर return一个陷阱:finally 里的 return

def risky():
    try:
        return 1 / 0
    finally:
        return "finally wins"      # swallows the ZeroDivisionError!

print(risky())
OUTPUT
finally wins
🕳️
finally Can Swallow Errorsfinally Errors निगल सकता हैfinally 会吞掉错误

A return, break or continue in finally replaces whatever was happening — including an exception in flight. The ZeroDivisionError above vanished without a trace. Since Python 3.14 (PEP 765) the compiler shows a SyntaxWarning for this. Keep finally for cleanup only.

finally में return, break या continue जो कुछ भी हो रहा था उसे बदल देता है — उड़ते हुए exception को भी। ऊपर का ZeroDivisionError बिना निशान ग़ायब हो गया। Python 3.14 (PEP 765) से compiler इसके लिए SyntaxWarning दिखाता है। finally सिर्फ़ cleanup के लिए रखें।

finally 中的 return、break 或 continue 会取代正在发生的一切 — 包括正在飞行的异常。上面的 ZeroDivisionError 消失得无影无踪。从 Python 3.14(PEP 765)起,编译器会对此发出 SyntaxWarning。finally 只用来做清理。


SectionSection第 08 节

Raising Exceptions — Setting Off Your Own Alarms 📣Exceptions Raise करना — अपने Alarm बजाना 📣抛出异常 — 拉响你自己的警报 📣

You don't only catch exceptions — you raise them too. When your function finds a problem it cannot fix, it should raise a clear, specific exception so the caller can decide what to do.

आप सिर्फ़ exceptions पकड़ते नहीं — raise भी करते हैं। जब आपके function को ऐसी समस्या मिले जिसे वह ठीक न कर सके, तो उसे एक साफ़, ख़ास exception raise करना चाहिए ताकि caller तय कर सके कि क्या करना है।

你不仅捕获异常 — 也会抛出异常。当函数发现自己无法解决的问题时,应该抛出一个清晰、具体的异常,让调用者决定怎么办。

def set_age(age):
    if not isinstance(age, int):
        raise TypeError(f"age must be int, got {type(age).__name__}")
    if age < 0 or age > 150:
        raise ValueError(f"age must be in 0..150, got {age}")
    return age

# Callers can choose to catch - or let the error bubble up
for value in [30, 200, "30"]:
    try:
        print("ok:", set_age(value))
    except ValueError as e:
        print(f"Invalid value: {e}")
    except TypeError as e:
        print(f"Wrong type: {e}")
OUTPUT
ok: 30 Invalid value: age must be in 0..150, got 200 Wrong type: age must be int, got str
🎯
Pick the Right Built-inसही Built-in चुनें选对内置异常

Wrong type of value → TypeError. Right type but bad value → ValueError. Missing key → KeyError. Not written yet → NotImplementedError. Callers already know these names, so they know how to catch them.

Value का ग़लत type → TypeError। सही type पर ख़राब value → ValueError। ग़ायब key → KeyError। अभी लिखा नहीं गया → NotImplementedError। Callers ये नाम पहले से जानते हैं, इसलिए उन्हें पकड़ना भी जानते हैं।

值的 类型 错了 → TypeError。类型对但 值 不对 → ValueError。缺少键 → KeyError。还没实现 → NotImplementedError。调用者早已熟悉这些名字,也就知道怎么捕获它们。

Re-raising — Catch, Log, RethrowRe-raise — पकड़ो, Log करो, फिर फेंको重新抛出 — 捕获、记录、再抛出

Sometimes you want to note that an error happened but still let it travel on. A bare raise inside an except block re-raises the current exception unchanged.

कभी आप दर्ज करना चाहते हैं कि error हुआ, पर उसे आगे भी जाने देना चाहते हैं। except block के अंदर खाली raise मौजूदा exception को बिना बदले फिर से raise करता है।

有时你想 记下 错误发生了,但仍让它继续传递。在 except 块中写一个单独的 raise,就会原样重新抛出当前异常。

import logging, sys
logging.basicConfig(stream=sys.stdout, format="%(levelname)s %(message)s")
logger = logging.getLogger("app")

def fetch():
    raise ConnectionError("server did not answer")

def load_data():
    try:
        return fetch()
    except ConnectionError as e:
        logger.warning(f"Connection failed: {e} - will retry higher up")
        raise                      # same exception continues bubbling

try:
    load_data()
except ConnectionError as e:
    print("caller got it too:", repr(e))
OUTPUT
WARNING Connection failed: server did not answer - will retry higher up caller got it too: ConnectionError('server did not answer')

SectionSection第 09 节

Exception Chaining — raise X from Y ⛓️Exception Chaining — raise X from Y ⛓️异常链 — raise X from Y ⛓️

When a low-level error causes a higher-level one, keep the whole chain so debugging shows the full story. raise ... from ... makes the link explicit. This program is meant to crash, so you can see the traceback:

जब कोई निचले स्तर का error ऊँचे स्तर का error पैदा करे, तो पूरी chain रखें ताकि debugging में पूरी कहानी दिखे। raise ... from ... यह कड़ी साफ़ करता है। यह program जान-बूझकर crash होता है, ताकि आप traceback देख सकें:

当底层错误引发上层错误时,要保留整条链,让调试时能看到完整经过。raise ... from ... 让这种关联一目了然。这个程序是 故意 崩溃的,好让你看到 traceback:

class ConfigError(Exception):
    """Raised when configuration cannot be loaded."""

def load_config(path):
    try:
        with open(path, encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError as e:
        # Chain: keep the original in the traceback
        raise ConfigError(f"Config not available at {path}") from e

load_config("/etc/app.conf")
OUTPUT (stderr)
Traceback (most recent call last): File "demo.py", line 6, in load_config with open(path, encoding="utf-8") as f: ~~~~^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: '/etc/app.conf' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "demo.py", line 12, in <module> load_config("/etc/app.conf") ~~~~~~~~~~~^^^^^^^^^^^^^^^^^ File "demo.py", line 10, in load_config raise ConfigError(f"Config not available at {path}") from e ConfigError: Config not available at /etc/app.conf
SyntaxSyntax语法Meaningअर्थ含义Traceback showsTraceback दिखाता हैTraceback 显示
raise NewError()A new exception; the original is still kept as contextनया exception; मूल फिर भी context के रूप में रहता है新异常;原始异常仍作为上下文保留"During handling of the above exception, another exception occurred"
raise NewError() from eA deliberate chain — the new one was caused by the oldजान-बूझकर chain — नया पुराने की वजह से हुआ有意的链 — 新异常由旧异常引起"The above exception was the direct cause of the following exception"
raise NewError() from NoneHide the original from the tracebackमूल को traceback से छिपाएँ在 traceback 中隐藏原始异常Only the new exceptionसिर्फ़ नया exception只有新异常

Proof: the three forms store the original in different attributes.

सबूत: तीनों रूप मूल error को अलग-अलग attributes में रखते हैं।

证明:三种写法把原始错误存在不同的属性里。

def convert(text):
    try:
        return int(text)
    except ValueError as e:
        if variant == "plain":
            raise RuntimeError("bad input")
        if variant == "from e":
            raise RuntimeError("bad input") from e
        raise RuntimeError("bad input") from None

for variant in ["plain", "from e", "from None"]:
    try:
        convert("abc")
    except RuntimeError as err:
        print(f"{variant:9} | __cause__={err.__cause__!r}")
        print(f"{'':9} | __context__={err.__context__!r}")
        print(f"{'':9} | __suppress_context__={err.__suppress_context__}")
OUTPUT
plain | __cause__=None | __context__=ValueError("invalid literal for int() with base 10: 'abc'") | __suppress_context__=False from e | __cause__=ValueError("invalid literal for int() with base 10: 'abc'") | __context__=ValueError("invalid literal for int() with base 10: 'abc'") | __suppress_context__=True from None | __cause__=None | __context__=ValueError("invalid literal for int() with base 10: 'abc'") | __suppress_context__=True
🔍
Why Chaining Beats SwallowingChaining निगलने से बेहतर क्यों为什么链接优于吞掉

Turning a FileNotFoundError into a domain-specific ConfigError is great API design — but if you drop the original, you lose the real file path from the operating system. raise ... from e keeps both, so future-you (or a teammate at 3 AM) can see exactly which file was missing.

FileNotFoundError को domain वाले ConfigError में बदलना बढ़िया API design है — पर अगर मूल को छोड़ दें, तो operating system से मिला असली file path खो जाता है। raise ... from e दोनों रखता है, ताकि भविष्य के आप (या रात 3 बजे कोई साथी) ठीक देख सकें कि कौन-सी file ग़ायब थी।

把 FileNotFoundError 转成领域专属的 ConfigError 是很好的 API 设计 — 但如果丢掉原始异常,就丢掉了操作系统给出的真实文件路径。raise ... from e 两者都保留,让未来的你(或凌晨 3 点的队友)能看清到底缺了哪个文件。


SectionSection第 10 节

Tracebacks — Following the Trail Back to the Real Error 🕵️Tracebacks — असली Error तक पीछे का रास्ता 🕵️Traceback — 顺着线索找到真正的错误 🕵️

The Detective's Trailजासूस का रास्ता侦探的足迹
A detective at a crime scene doesn't just look at the broken window — they follow the footprints back through the house, out of the door, down the driveway, to where it all began.

A Python traceback is that trail. When an exception is raised, Python records every function call that led there and prints them in order — the starting point at the top, the actual crime scene at the bottom. Reading this trail is the most important debugging skill in Python.
अपराध की जगह पहुँचा जासूस सिर्फ़ टूटी खिड़की नहीं देखता — वह पैरों के निशान घर के अंदर से, दरवाज़े से बाहर, रास्ते पर, उस जगह तक पीछे-पीछे जाता है जहाँ से सब शुरू हुआ।

Python का traceback वही रास्ता है। जब exception raise होता है, Python वहाँ तक पहुँचाने वाली हर function call दर्ज करता है और उन्हें क्रम से छापता है — शुरुआत ऊपर, असली घटना-स्थल नीचे। यह रास्ता पढ़ना Python में debugging का सबसे ज़रूरी हुनर है।
到达犯罪现场的侦探不会只盯着破碎的窗户 — 他会顺着脚印穿过房子、走出大门、沿着车道,一路追溯到一切开始的地方。

Python 的 traceback 就是这条足迹。抛出异常时,Python 记录下通往那里的每一次函数调用,并按顺序打印 — 起点在上,真正的案发现场在下。读懂这条足迹是 Python 中最重要的调试技能。

Anatomy of a Traceback — Read It Bottom-UpTraceback की बनावट — नीचे से ऊपर पढ़ेंTraceback 的结构 — 从下往上读

Traceback (most recent call last):            <- header
  File "demo.py", line 11, in <module>        | 1. where it all started
    print("Total:", total(cart))
                    ~~~~~^^^^^^
  File "demo.py", line 7, in total            | 2. next call down
    s += price_of(item)
         ~~~~~~~~^^^^^^
  File "demo.py", line 2, in price_of         | 3. THE CRIME SCENE
    return item["cost"] * item["qty"]
           ~~~~^^^^^^^^                       |    the part that failed
KeyError: 'cost'                              <- WHAT went wrong
🔎 How to Read a Traceback in Three StepsTraceback तीन कदमों में कैसे पढ़ें三步读懂 traceback
Step 1कदम 1第 1 步
Read the last line first. It names the exception class and the message. That is what went wrong. पहले आख़िरी line पढ़ें। उसमें exception class और संदेश है। यही है क्या ग़लत हुआ। 先读 最后一行。它给出异常类和信息。这就是 出了什么错。
Step 2कदम 2第 2 步
Read the File entry just above it: the file, line number and code where the exception was raised. That is where. उसके ठीक ऊपर की File entry पढ़ें: file, line number और वह code जहाँ exception raise हुआ। यही है कहाँ। 读 紧挨着上面的 File 条目:抛出异常的文件、行号和代码。这就是 在哪里。
Step 3कदम 3第 3 步
Read upward through the earlier entries — the chain of calls that led there. That is how you got there. पहले की entries में ऊपर की ओर पढ़ें — वहाँ तक ले जाने वाली calls की chain। यही है आप वहाँ कैसे पहुँचे। 向上 阅读前面的条目 — 通往那里的调用链。这就是 你是怎么走到这里的。

Try It — Traceback Readerकरके देखें — Traceback Reader动手试试 — Traceback 阅读器

Three real programs, three real crashes. Press the steps in order and watch the traceback and the code light up together.

तीन असली programs, तीन असली crashes। कदम क्रम से दबाएँ और traceback व code को एक साथ जलते देखें।

三个真实程序,三次真实崩溃。按顺序点击步骤,看 traceback 和代码一起亮起。

🕵️ Traceback ReaderLast line first. Then up. Then fix.पहले आख़िरी line। फिर ऊपर। फिर इलाज।先看最后一行,再往上看,然后修复。
demo.pydemo.pydemo.py
terminalterminal终端
📚
"Most Recent Call Last" — The Direction Convention"Most Recent Call Last" — दिशा का नियम"Most Recent Call Last" — 方向约定

The header "most recent call last" tells you the traceback is in time order — the top is where execution began, the bottom is where it ended in flames. Beginners read top-down looking for the error. Train yourself to jump to the last line first.

Header "most recent call last" बताता है कि traceback समय के क्रम में है — ऊपर वह जगह जहाँ execution शुरू हुआ, नीचे वह जहाँ वह आग में ख़त्म हुआ। शुरुआती लोग error ढूँढने के लिए ऊपर से नीचे पढ़ते हैं। ख़ुद को पहले आख़िरी line पर जाने की आदत डालें।

标题 "most recent call last" 说明 traceback 按时间顺序排列 — 顶部是执行开始的地方,底部是执行在火焰中结束的地方。初学者习惯从上往下找错误。训练自己先跳到最后一行。

The Problem — Catching Hides the Tracebackसमस्या — पकड़ने से Traceback छिप जाता है问题 — 捕获会隐藏 traceback

Once you catch an exception, its traceback no longer appears in the terminal — handling it is now your job. But that is often exactly when you want to see it:

एक बार आप exception पकड़ लें, तो उसका traceback terminal में नहीं दिखता — उसे संभालना अब आपका काम है। पर अक्सर ठीक तभी आप उसे देखना चाहते हैं:

一旦捕获了异常,它的 traceback 就不会再出现在终端里 — 处理它成了你的事。但往往正是这时你 想 看到它:

def price_of(item):
    return item["cost"] * item["qty"]

def total(order):
    return sum(price_of(item) for item in order)

def process(order):
    try:
        return total(order)
    except Exception as e:
        print(f"something failed: {e}")      # USELESS - just the message
        return None

process([{"cost": 10, "qty": 2}, {"qty": 3}])
OUTPUT (useless — no idea WHERE it broke)
something failed: 'cost'

SectionSection第 11 节

The traceback Module — Seeing the Real Error 🔬traceback Module — असली Error देखना 🔬traceback 模块 — 看清真正的错误 🔬

The built-in traceback module prints or captures the full traceback even after you have caught the exception. It is the difference between a useless "something failed" and a pointer to the exact line.

Built-in traceback module exception पकड़ने के बाद भी पूरा traceback छाप या सहेज सकता है। यही फ़र्क़ है बेकार "something failed" और ठीक उस line की ओर इशारे में।

内置的 traceback 模块即使在捕获异常之后,也能打印或保存完整的 traceback。这就是毫无用处的 "something failed" 与精确指向出错行之间的区别。

FunctionFunction函数What it doesक्या करता है作用When to useकब इस्तेमाल करें何时使用
traceback.print_exc()Prints the current exception's full traceback to stderrमौजूदा exception का पूरा traceback stderr पर छापता है把当前异常的完整 traceback 打印到 stderrQuick debugging inside exceptexcept के अंदर जल्दी debugging在 except 中快速调试
traceback.format_exc()Returns the same text as a strवही text str के रूप में लौटाता है以 str 返回同样的文本Save to a file, a log, an emailFile, log, email में सहेजना保存到文件、日志、邮件
traceback.print_tb(tb)Prints only the call chain (no exception line)सिर्फ़ call chain छापता है (exception line नहीं)只打印调用链(没有异常行)When you already show the exception separatelyजब exception अलग से दिखा रहे हों当你已单独显示异常时
traceback.format_exception(e)Returns a list of strings for an exception object (one-argument form: 3.10+)Exception object के लिए strings की list लौटाता है (एक-argument रूप: 3.10+)为异常对象返回字符串列表(单参数形式:3.10+)Custom formatting, or after the except blockअपनी formatting, या except block के बाद自定义格式,或在 except 块之后

Example — print_exc(): See What You Caughtउदाहरण — print_exc(): जो पकड़ा वह देखें示例 — print_exc():看清你捕获了什么

import traceback

def price_of(item):
    return item["cost"] * item["qty"]

def total(order):
    return sum(price_of(item) for item in order)

order = [{"cost": 10, "qty": 2}, {"qty": 3}]   # second item missing 'cost'

try:
    print("Total:", total(order))
except Exception:
    print("--- caught, but let me show you the real error ---")
    traceback.print_exc()
    print("--- carrying on ---")
OUTPUT
--- caught, but let me show you the real error --- Traceback (most recent call last): File "demo.py", line 12, in <module> print("Total:", total(order)) ~~~~~^^^^^^^ File "demo.py", line 7, in total return sum(price_of(item) for item in order) File "demo.py", line 7, in <genexpr> return sum(price_of(item) for item in order) ~~~~~~~~^^^^^^ File "demo.py", line 4, in price_of return item["cost"] * item["qty"] ~~~~^^^^^^^^ KeyError: 'cost' --- carrying on ---
🏆
Now You Actually Know What Brokeअब आपको सच में पता है क्या टूटा现在你真的知道哪里坏了

One line — traceback.print_exc() — turned the useless "something failed: 'cost'" into the full call chain: <module> → total → <genexpr> (the generator inside sum()) → price_of, ending on the exact line. That is the difference between guessing and knowing.

एक line — traceback.print_exc() — ने बेकार "something failed: 'cost'" को पूरी call chain में बदल दिया: <module> → total → <genexpr> (sum() के अंदर का generator) → price_of, ठीक उसी line पर ख़त्म। यही फ़र्क़ है अंदाज़े और जानकारी में।

一行 traceback.print_exc(),就把毫无用处的 "something failed: 'cost'" 变成了完整调用链:<module> → total → <genexpr>(sum() 里的生成器)→ price_of,最后落在确切的那一行。这就是猜测与确知的区别。

Example — format_exc(): Send It to a Fileउदाहरण — format_exc(): File में भेजें示例 — format_exc():写入文件

print_exc() writes to the terminal, which is useless for a server nobody is watching. format_exc() returns the same traceback as a string — perfect for a log file.

print_exc() terminal पर लिखता है, जो ऐसे server के लिए बेकार है जिसे कोई देख नहीं रहा। format_exc() वही traceback string के रूप में लौटाता है — log file के लिए बिल्कुल सही।

print_exc() 写到终端,对没人盯着的服务器毫无用处。format_exc() 以字符串返回同样的 traceback — 非常适合写进日志文件。

import traceback, datetime
from pathlib import Path

def safe_run(func, *args):
    try:
        return func(*args)
    except Exception:
        tb_text = traceback.format_exc()          # full traceback as a string
        now = datetime.datetime.now().isoformat(timespec="seconds")
        with open("errors.log", "a", encoding="utf-8") as f:
            f.write(f"===== {now} =====\n")
            f.write(tb_text)
        print("An error occurred. Details written to errors.log")
        return None

def average(nums):
    return sum(nums) / len(nums)

print(safe_run(average, [4, 8]))
print(safe_run(average, []))                      # ZeroDivisionError
print(Path("errors.log").read_text(encoding="utf-8"), end="")
OUTPUT
6.0 An error occurred. Details written to errors.log None ===== 2026-10-30T16:57:11 ===== Traceback (most recent call last): File "demo.py", line 6, in safe_run return func(*args) File "demo.py", line 17, in average return sum(nums) / len(nums) ~~~~~~~~~~^~~~~~~~~~~ ZeroDivisionError: division by zero

(The timestamp is from the moment this page was built, in UTC.)

(Timestamp उस समय का है जब यह page बना, UTC में।)

(时间戳是本页面构建时的 UTC 时间。)


SectionSection第 12 节

The Production Pattern — logging.exception() 📝Production का तरीका — logging.exception() 📝生产环境的做法 — logging.exception() 📝

Real applications rarely call traceback.print_exc(). They use the logging module, which has a method made exactly for this: logger.exception() logs your message at ERROR level plus the full traceback.

असली applications शायद ही traceback.print_exc() बुलाते हैं। वे logging module इस्तेमाल करते हैं, जिसमें ठीक इसी काम का method है: logger.exception() आपका संदेश ERROR level पर और पूरा traceback log करता है।

真实应用很少直接调用 traceback.print_exc()。它们使用 logging 模块,其中有专为此设计的方法:logger.exception() 以 ERROR 级别记录你的信息,外加 完整的 traceback。

🚫 Weak — logger.error(str(e))कमज़ोर — logger.error(str(e))薄弱 — logger.error(str(e))
What gets loggedक्या log होता है记录了什么Resultनतीजा结果
The exception's messageException का संदेश异常信息Just: 'cost'बस: 'cost'只有:'cost'
Location in codeCode में जगह代码位置Lostखो गई丢失
Call chainCall chain调用链Lostखो गई丢失
DebuggingDebugging调试Guess and checkअंदाज़ा लगाओ और जाँचो靠猜
✅ Strong — logger.exception(msg)मज़बूत — logger.exception(msg)可靠 — logger.exception(msg)
What gets loggedक्या log होता है记录了什么Resultनतीजा结果
Your messageआपका संदेश你的信息Human-readable contextपढ़ने लायक संदर्भ可读的上下文
Exception class + messageException class + संदेश异常类 + 信息Included automaticallyअपने-आप शामिल自动包含
Full tracebackपूरा traceback完整 tracebackAttached automaticallyअपने-आप जुड़ा自动附上
DebuggingDebugging调试Jump straight to the lineसीधे line पर पहुँचें直接跳到出错行
import logging, sys

logging.basicConfig(stream=sys.stdout, level=logging.INFO,
                    format="%(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def price_of(item):
    return item["cost"] * item["qty"]

def total(order):
    return sum(price_of(item) for item in order)

def process(order):
    try:
        return total(order)
    except Exception:
        logger.exception("failed to process order")      # msg + traceback
        return None

process([{"cost": 10, "qty": 2}, {"qty": 3}])
OUTPUT
ERROR failed to process order Traceback (most recent call last): File "demo.py", line 15, in process return total(order) File "demo.py", line 11, in total return sum(price_of(item) for item in order) File "demo.py", line 11, in <genexpr> return sum(price_of(item) for item in order) ~~~~~~~~^^^^^^ File "demo.py", line 8, in price_of return item["cost"] * item["qty"] ~~~~^^^^^^^^ KeyError: 'cost'
🔑
Two Rules for logger.exception()logger.exception() के दो नियमlogger.exception() 的两条规则

(1) Call it inside an except block. Outside one there is no current exception, so the log gets a useless NoneType: None instead of a traceback. (2) Pass a context message, not the exception itself. The traceback already carries the exception; your message adds what the program was trying to do.

(1) इसे except block के अंदर बुलाएँ। उसके बाहर कोई मौजूदा exception नहीं होता, इसलिए log में traceback की जगह बेकार NoneType: None आता है। (2) संदर्भ वाला संदेश दें, exception ख़ुद नहीं। Traceback में exception पहले से है; आपका संदेश बताता है कि program क्या करने की कोशिश कर रहा था।

(1) 在 except 块 内部 调用它。在外面没有当前异常,日志里只会出现无用的 NoneType: None 而不是 traceback。(2) 传入 上下文 信息,而不是异常本身。traceback 已经带上了异常;你的信息补充的是程序当时在做什么。

Looking Inside an Exception ObjectException Object के अंदर झाँकना查看异常对象内部

Every caught exception carries its details: args, its type, and __traceback__ — the traceback object, which you can pass to traceback.print_tb() or keep for later.

हर पकड़ा गया exception अपनी जानकारी साथ रखता है: args, उसका type, और __traceback__ — traceback object, जिसे आप traceback.print_tb() को दे सकते हैं या बाद के लिए रख सकते हैं।

每个被捕获的异常都带着它的细节:args、类型,以及 __traceback__ — traceback 对象,你可以把它传给 traceback.print_tb() 或留着以后用。

import traceback

def price_of(item):
    return item["cost"] * item["qty"]

try:
    price_of({"qty": 3})
except Exception as e:
    print("class   :", type(e).__name__)
    print("message :", e)
    print("args    :", e.args)
    print("tb obj  :", type(e.__traceback__).__name__, "- line", e.__traceback__.tb_lineno)
    print("--- frames only (print_tb) ---")
    traceback.print_tb(e.__traceback__)
OUTPUT
class : KeyError message : 'cost' args : ('cost',) tb obj : traceback - line 7 --- frames only (print_tb) --- File "demo.py", line 7, in <module> price_of({"qty": 3}) ~~~~~~~~^^^^^^^^^^^^ File "demo.py", line 4, in price_of return item["cost"] * item["qty"] ~~~~^^^^^^^^

SectionSection第 13 节

Custom Exceptions — Your Own Alarm Classes 🏷️Custom Exceptions — आपकी अपनी Alarm Classes 🏷️自定义异常 — 你自己的警报类 🏷️

Built-in exceptions cover general problems. Problems specific to your program deserve their own classes. A custom exception is just a class that inherits from Exception (or one of its children).

Built-in exceptions आम समस्याएँ संभालती हैं। आपके program की ख़ास समस्याओं को अपनी classes मिलनी चाहिए। Custom exception बस एक class है जो Exception (या उसके किसी child) से inherit करती है।

内置异常覆盖的是通用问题。你程序特有的问题值得拥有自己的类。自定义异常就是一个继承 Exception(或其子类)的类。

# A small hierarchy for a payment library
class PaymentError(Exception):
    """Base class for all payment-related failures."""

class InsufficientFundsError(PaymentError):
    def __init__(self, balance, amount):
        super().__init__(f"Need {amount}, only {balance} available")
        self.balance = balance
        self.amount = amount

class CardDeclinedError(PaymentError):
    def __init__(self, reason):
        super().__init__(f"Card declined: {reason}")
        self.reason = reason

def charge(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    if amount > 10000:
        raise CardDeclinedError("amount exceeds daily limit")
    return balance - amount

# Callers can catch broad or narrow
for balance, amount in [(500, 1200), (50000, 20000), (900, 100)]:
    try:
        print("new balance:", charge(balance, amount))
    except InsufficientFundsError as e:
        print(f"Top up needed: short by {e.amount - e.balance}")
    except PaymentError as e:
        print(f"Some other payment issue: {e}")
OUTPUT
Top up needed: short by 700 Some other payment issue: Card declined: amount exceeds daily limit new balance: 800
🏆
The Right Base Classसही Base Class正确的基类

Inherit custom exceptions from Exception — not BaseException. For a library, define one base class (PaymentError) and put every specific error under it. Callers can then catch the whole family with one except PaymentError: or pick a single child. Try InsufficientFundsError in the Hierarchy Explorer (Section 02) — it sits in the same tree.

Custom exceptions को Exception से inherit करें — BaseException से नहीं। Library के लिए एक base class (PaymentError) बनाएँ और हर ख़ास error उसके नीचे रखें। तब callers एक except PaymentError: से पूरा परिवार पकड़ सकते हैं या एक child चुन सकते हैं। Hierarchy Explorer (Section 02) में InsufficientFundsError आज़माएँ — वह उसी tree में है।

自定义异常要继承 Exception — 而不是 BaseException。对于一个库,定义一个基类(PaymentError),把所有具体错误都放在它下面。调用者就能用一个 except PaymentError: 捕获整个家族,或只挑一个子类。在 Hierarchy Explorer(第 02 节)里试试 InsufficientFundsError — 它就在同一棵树上。


SectionSection第 14 节

Modern Tools — Notes, Exception Groups and EAFP 🧰आधुनिक औज़ार — Notes, Exception Groups और EAFP 🧰现代工具 — 注释、异常组与 EAFP 🧰

Python 3.11 added two useful tools. add_note() attaches extra context to an exception without changing its type. ExceptionGroup raises several errors at once, and except* handles each kind separately — handy for form validation or many tasks running together.

Python 3.11 ने दो काम के औज़ार जोड़े। add_note() exception का type बदले बिना उसमें अतिरिक्त संदर्भ जोड़ता है। ExceptionGroup एक साथ कई errors raise करता है, और except* हर तरह को अलग-अलग संभालता है — form validation या साथ चल रहे कई tasks के लिए उपयोगी।

Python 3.11 新增了两个实用工具。add_note() 在不改变异常类型的情况下附加额外上下文。ExceptionGroup 一次抛出 多个 错误,except* 分别处理每一类 — 适合表单校验或同时运行的多个任务。

# Python 3.11+: add context to an error with add_note()
def load_row(n, row):
    try:
        return int(row)
    except ValueError as e:
        e.add_note(f"while reading row {n} of marks.csv")
        raise

try:
    load_row(7, "ninety")
except ValueError as e:
    print(e)
    print(e.__notes__)

# Python 3.11+: report SEVERAL errors at once with ExceptionGroup
def validate(form):
    errors = []
    if not form.get("name"):
        errors.append(ValueError("name is empty"))
    if "@" not in form.get("email", ""):
        errors.append(ValueError("email has no @"))
    if not isinstance(form.get("age"), int):
        errors.append(TypeError("age must be a number"))
    if errors:
        raise ExceptionGroup("form is invalid", errors)

try:
    validate({"name": "", "email": "aarav.example.com", "age": "20"})
except* ValueError as group:
    for err in group.exceptions:
        print("value problem:", err)
except* TypeError as group:
    for err in group.exceptions:
        print("type problem: ", err)
OUTPUT
invalid literal for int() with base 10: 'ninety' ['while reading row 7 of marks.csv'] value problem: name is empty value problem: email has no @ type problem: age must be a number

When a note is attached and the error is not caught, the note is printed under the last line of the traceback — so whoever reads it knows which row of the file was bad.

जब note जुड़ा हो और error पकड़ा न जाए, तो note traceback की आख़िरी line के नीचे छपता है — ताकि पढ़ने वाले को पता चले कि file की कौन-सी row ख़राब थी।

附加了注释的错误如果没被捕获,注释会打印在 traceback 最后一行下面 — 读的人就知道文件的哪一行有问题。

EAFP vs LBYL — Two StylesEAFP बनाम LBYL — दो तरीकेEAFP 与 LBYL — 两种风格

Python programmers often prefer EAFP — "Easier to Ask Forgiveness than Permission": just try it and handle the exception. The other style, LBYL — "Look Before You Leap" — checks first. Both are fine; for a normal, expected case, a tool like dict.get() is often clearer than either.

Python programmers अक्सर EAFP पसंद करते हैं — "Permission से माफ़ी माँगना आसान": बस कोशिश करो और exception संभालो। दूसरा तरीका, LBYL — "कूदने से पहले देखो" — पहले जाँचता है। दोनों ठीक हैं; सामान्य, उम्मीद वाले मामले में dict.get() जैसा औज़ार अक्सर दोनों से साफ़ होता है।

Python 程序员常偏爱 EAFP —"请求原谅比请求许可更容易":直接尝试,再处理异常。另一种风格 LBYL —"三思而后行"— 先检查。两者都可以;对于正常、预料之中的情况,像 dict.get() 这样的工具往往比两者都清晰。

prices = {"apple": 30, "banana": 10}

# LBYL - "Look Before You Leap": check first
if "mango" in prices:
    print(prices["mango"])
else:
    print("LBYL: no mango")

# EAFP - "Easier to Ask Forgiveness than Permission": try, then handle
try:
    print(prices["mango"])
except KeyError:
    print("EAFP: no mango")

# For a missing key that is NORMAL, the simplest tool is often neither:
print("get():", prices.get("mango", "no mango"))
OUTPUT
LBYL: no mango EAFP: no mango get(): no mango
⚖️
When to Prefer Whichकब कौन-सा何时选哪种

Use EAFP when the check and the action could get out of sync — for example, a file could be deleted between os.path.exists() and open(), so just open() and catch FileNotFoundError. Use a plain check or a default value when the "missing" case is normal and frequent. Since Python 3.11, a try block costs almost nothing when no exception is raised; raising and catching does cost a little, so avoid it in very hot loops.

EAFP तब इस्तेमाल करें जब जाँच और काम के बीच हालात बदल सकते हों — जैसे os.path.exists() और open() के बीच file delete हो सकती है, इसलिए सीधे open() करें और FileNotFoundError पकड़ें। जब "ग़ायब" होना सामान्य और बार-बार होने वाला हो, तो सीधी जाँच या default value इस्तेमाल करें। Python 3.11 से, exception न होने पर try block लगभग मुफ़्त है; raise करना और पकड़ना थोड़ा ख़र्च करता है, इसलिए बहुत तेज़ चलने वाले loops में इससे बचें।

当检查和操作之间情况可能发生变化时用 EAFP — 比如文件可能在 os.path.exists() 和 open() 之间被删除,所以直接 open() 并捕获 FileNotFoundError。当"缺失"是正常且频繁的情况时,用简单检查或默认值。从 Python 3.11 起,没有异常时 try 块几乎零开销;抛出并捕获则有少量开销,所以在非常密集的循环里要避免。


SectionSection第 15 节

Common Pitfalls — Anti-Patterns Every Codebase Has 🕳️आम गड्ढे — हर Codebase की बुरी आदतें 🕳️常见陷阱 — 每个代码库都有的反模式 🕳️

💩
Bare except:खाली except:裸 except:
catches EVERYTHINGसब कुछ पकड़ता है捕获一切
A bare except: also catches KeyboardInterrupt and SystemExit. Users can't Ctrl+C, sys.exit() gets swallowed, bugs go silent. Don't write it.
खाली except:, KeyboardInterrupt और SystemExit भी पकड़ता है। Users Ctrl+C नहीं कर पाते, sys.exit() निगल लिया जाता है, bugs चुप हो जाते हैं। इसे न लिखें।
裸 except: 还会捕获 KeyboardInterrupt 和 SystemExit。用户无法 Ctrl+C,sys.exit() 被吞掉,bug 悄无声息。不要这样写。
💩
Catching Exception by reflexआदतन Exception पकड़ना习惯性捕获 Exception
hides real bugsअसली bugs छिपाता है掩盖真正的 bug
except Exception: lets Ctrl+C through, but it still buries coding mistakes (TypeError, AttributeError) under a vague "something went wrong". Catch what you can actually handle.
except Exception: Ctrl+C को जाने देता है, पर फिर भी coding की ग़लतियों (TypeError, AttributeError) को धुँधले "कुछ ग़लत हुआ" के नीचे दबा देता है। वही पकड़ें जो आप सच में संभाल सकें।
except Exception: 会放过 Ctrl+C,但仍会把编码错误(TypeError、AttributeError)埋在含糊的"出了点问题"之下。只捕获你真正能处理的。
💩
Silent except: passचुप except: pass沉默的 except: pass
the debugger's nightmaredebugger का बुरा सपना调试者的噩梦
An empty handler throws the error away with no record. Six months later, feature X "just doesn't work" and nobody knows why. At least log it with logger.exception().
ख़ाली handler error को बिना किसी रिकॉर्ड के फेंक देता है। छह महीने बाद feature X "बस काम नहीं करता" और किसी को पता नहीं क्यों। कम से कम logger.exception() से log करें।
空处理器把错误丢掉,不留任何记录。六个月后,功能 X"就是不工作",没人知道为什么。至少用 logger.exception() 记录一下。
💩
Printing str(e) onlyसिर्फ़ str(e) छापना只打印 str(e)
loses the locationजगह खो जाती है丢失位置
print(f"failed: {e}") gives the message but not where. Use traceback.print_exc() or logger.exception() to keep the file, line and call chain.
print(f"failed: {e}") संदेश देता है पर कहाँ नहीं। File, line और call chain रखने के लिए traceback.print_exc() या logger.exception() इस्तेमाल करें।
print(f"failed: {e}") 只给出信息,没有 位置。用 traceback.print_exc() 或 logger.exception() 保留文件、行号和调用链。
💩
Too much inside trytry में बहुत ज़्यादाtry 里放太多
imprecise handlingअस्पष्ट handling处理不精确
A 50-line try block catches errors from lines you never meant to guard. Keep try tight — just the risky call — and move the rest to else.
50 lines का try block उन lines के errors भी पकड़ता है जिन्हें आप बचाना नहीं चाहते थे। try छोटा रखें — सिर्फ़ जोखिम वाली call — और बाक़ी else में ले जाएँ।
50 行的 try 块会捕获你从没打算保护的行的错误。让 try 保持紧凑 — 只放风险调用 — 其余移到 else。
💩
Losing the original errorमूल error खो देना丢失原始错误
unchained raiseबिना chain का raise未链接的 raise
Inside except, prefer raise MyError("failed") from e. It marks the original as the direct cause, so the traceback tells the real story.
except के अंदर raise MyError("failed") from e को तरजीह दें। यह मूल को सीधा कारण बताता है, ताकि traceback असली कहानी बताए।
在 except 中优先写 raise MyError("failed") from e。它把原始错误标为直接原因,让 traceback 讲出真实经过。

SectionSection第 16 节

A Complete Real-World Example — A Robust API Client 🌐पूरा असली उदाहरण — एक मज़बूत API Client 🌐完整实战示例 — 健壮的 API 客户端 🌐

This function fetches a user from a server, parses the JSON and saves it to disk. It uses almost everything in this tutorial: specific catches, a custom exception family, chaining with from e, else for the success path and finally for cleanup. A tiny fake server makes it runnable, so the output below is real — four users, four different paths.

यह function server से एक user लाता है, JSON parse करता है और उसे disk पर save करता है। इसमें इस tutorial की लगभग हर चीज़ है: ख़ास catches, custom exception परिवार, from e से chaining, सफलता के लिए else और cleanup के लिए finally। एक छोटा नक़ली server इसे चलने लायक बनाता है, इसलिए नीचे का output असली है — चार users, चार अलग रास्ते।

这个函数从服务器获取用户、解析 JSON 并保存到磁盘。它用到了本教程几乎所有内容:具体的捕获、自定义异常家族、用 from e 链接、用 else 走成功路径、用 finally 做清理。一个小小的假服务器让它可以运行,所以下面的输出是真实的 — 四个用户,四条不同路径。

import json, logging, sys
from pathlib import Path

logging.basicConfig(stream=sys.stdout, format="%(levelname)s %(message)s")
logger = logging.getLogger("client")

class ApiClientError(Exception):
    """Base for all API client errors."""

class UserNotFound(ApiClientError):
    """The requested user does not exist."""

class MalformedResponse(ApiClientError):
    """Server returned something we cannot parse."""

# A fake server so this example runs anywhere
SERVER = {"/users/1": '{"name": "Aarav", "city": "Baddi"}', "/users/2": "{oops"}

class Connection:
    def fetch(self, url):
        if url == "/users/9":
            raise ConnectionError("network unreachable")
        return SERVER.get(url)
    def close(self):
        print("   (connection closed)")

def save_user_profile(user_id, out_path):
    conn = None
    try:
        conn = Connection()
        raw = conn.fetch(f"/users/{user_id}")
        if raw is None:
            raise UserNotFound(f"No user with id {user_id}")
        try:
            data = json.loads(raw)
        except json.JSONDecodeError as e:
            raise MalformedResponse("bad JSON from server") from e
    except ConnectionError as e:
        logger.error(f"network problem: {e}")   # expected: short message
        return False
    except UserNotFound as e:
        logger.warning(str(e))                   # expected, no traceback needed
        return False
    else:
        # Runs only if the try block succeeded
        try:
            Path(out_path).write_text(json.dumps(data, indent=2), encoding="utf-8")
        except OSError as e:
            raise ApiClientError(f"could not save profile: {e}") from e
        print(f"   saved profile to {out_path}")
        return True
    finally:
        # Always release the connection, success or failure
        if conn is not None:
            conn.close()

for uid in [1, 3, 9, 2]:
    print(f"user {uid}:")
    try:
        print("   result:", save_user_profile(uid, f"user{uid}.json"))
    except ApiClientError as e:
        print(f"   ApiClientError: {e} (caused by {type(e.__cause__).__name__})")
OUTPUT
user 1: saved profile to user1.json (connection closed) result: True user 3: WARNING No user with id 3 (connection closed) result: False user 9: ERROR network problem: network unreachable (connection closed) result: False user 2: (connection closed) ApiClientError: bad JSON from server (caused by JSONDecodeError)
UserUser用户What happenedक्या हुआ发生了什么Which clauseकौन-सा clause哪个子句
1Fetched, parsed and savedलाया, parse किया, save किया获取、解析并保存else → finally
3Server has no such userServer पर ऐसा user नहीं服务器上没有该用户except UserNotFound → finally
9Network failedNetwork fail हुआ网络失败except ConnectionError → finally
2Bad JSON → MalformedResponse (chained). No handler in the function matches, so it reaches the callerख़राब JSON → MalformedResponse (chained)। Function में कोई handler match नहीं करता, इसलिए यह caller तक पहुँचता है坏 JSON → MalformedResponse(已链接)。函数中没有匹配的处理器,于是传到调用者finally → callercaller调用者
🏆
What Makes This Production-Gradeइसे Production लायक क्या बनाता है是什么让它达到生产级

Expected problems (user not found, network down) are caught by name and logged briefly. The inner try around json.loads turns a low-level error into a domain error with from e, so the cause is kept. The success path lives in else. finally guarantees the connection closes — you can see "(connection closed)" in all four runs. In a real service you would also log unexpected errors with logger.exception() so the traceback lands in the log file.

उम्मीद वाली समस्याएँ (user नहीं मिला, network बंद) नाम से पकड़ी जाती हैं और छोटे में log होती हैं। json.loads के चारों ओर का अंदरूनी try निचले स्तर के error को from e के साथ domain error में बदलता है, ताकि कारण बना रहे। सफलता का रास्ता else में है। finally पक्का करता है कि connection बंद हो — चारों runs में "(connection closed)" दिखता है। असली service में आप अनपेक्षित errors को logger.exception() से भी log करेंगे ताकि traceback log file में पहुँचे।

预料之中的问题(用户不存在、网络断开)按名字捕获并简短记录。包住 json.loads 的内层 try 用 from e 把底层错误转成领域错误,原因得以保留。成功路径在 else 中。finally 保证连接关闭 — 四次运行里都能看到 "(connection closed)"。在真实服务中,你还应当用 logger.exception() 记录意外错误,让 traceback 写进日志文件。


SectionSection第 17 节

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

🔥 Exception Handling — Rules to RememberException Handling — याद रखने वाले नियम异常处理 — 必须记住的规则
1
Never write a bare except:. It catches KeyboardInterrupt and SystemExit. If you truly need a broad catch, use except Exception:.
कभी खाली except: न लिखें। यह KeyboardInterrupt और SystemExit पकड़ता है। अगर सच में बड़ा catch चाहिए, तो except Exception: लिखें।
永远不要写裸 except:。它会捕获 KeyboardInterrupt 和 SystemExit。如果确实需要宽泛捕获,用 except Exception:。
2
Catch the narrowest exception you can handle. FileNotFoundError beats OSError beats Exception.
सबसे संकरा exception पकड़ें जिसे आप संभाल सकें। FileNotFoundError बेहतर है OSError से, जो बेहतर है Exception से।
捕获你能处理的最窄的异常。FileNotFoundError 胜过 OSError,OSError 胜过 Exception。
3
Specific before general. Python runs the first matching except; a broad one on top makes the rest dead code.
ख़ास पहले, आम बाद में। Python पहला match करता except चलाता है; ऊपर रखा बड़ा वाला बाक़ी को dead code बना देता है।
先具体后宽泛。Python 运行第一个匹配的 except;宽泛的放在上面会让其余的变成死代码。
4
Keep try blocks tight. Guard the risky call, not fifty lines of logic.
try blocks छोटे रखें। जोखिम वाली call बचाएँ, पचास lines का logic नहीं।
让 try 块保持紧凑。保护风险调用,而不是五十行逻辑。
5
Use else for the success path and finally for cleanup that must always happen. Never return from finally.
सफलता के रास्ते के लिए else और हमेशा होने वाले cleanup के लिए finally इस्तेमाल करें। finally से कभी return न करें।
成功路径用 else,必须执行的清理用 finally。永远不要在 finally 里 return。
6
Read tracebacks bottom-up: last line = what, the entry above = where, the rest = how you got there.
Tracebacks नीचे से ऊपर पढ़ें: आख़िरी line = क्या, उसके ऊपर की entry = कहाँ, बाक़ी = आप वहाँ कैसे पहुँचे।
从下往上读 traceback:最后一行 = 什么,上面的条目 = 在哪里,其余 = 怎么走到这里。
7
Never log just str(e). Inside except, use logger.exception("what we were doing") to keep the full traceback.
सिर्फ़ str(e) कभी log न करें। except के अंदर पूरा traceback रखने के लिए logger.exception("हम क्या कर रहे थे") इस्तेमाल करें।
永远不要只记录 str(e)。在 except 中用 logger.exception("当时在做什么") 保留完整 traceback。
8
Chain with raise X from e when you turn a low-level error into your own error type.
जब निचले स्तर के error को अपने error type में बदलें, तो raise X from e से chain करें।
把底层错误转成自己的错误类型时,用 raise X from e 链接。
9
Custom exceptions inherit from Exception, with one base class per library so callers can catch broadly or narrowly.
Custom exceptions Exception से inherit करें, हर library के लिए एक base class के साथ, ताकि callers बड़ा या छोटा पकड़ सकें।
自定义异常继承 Exception,每个库一个基类,让调用者可宽可窄地捕获。
10
Exceptions are for problems, not routine branching. EAFP (try first) is Pythonic, but for a normal, frequent case like a missing key, a default such as dict.get() is often clearer.
Exceptions समस्याओं के लिए हैं, रोज़ की branching के लिए नहीं। EAFP (पहले कोशिश) Pythonic है, पर ग़ायब key जैसे सामान्य, बार-बार वाले मामले में dict.get() जैसा default अक्सर ज़्यादा साफ़ होता है।
异常用于问题,而不是日常分支。EAFP(先尝试)很 Pythonic,但对于缺少键这类正常且频繁的情况,像 dict.get() 这样的默认值往往更清晰。

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

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

Exceptions are alarms that travel up the call stack until a matching except answers them. Catch narrowly and in the right order, keep try small, put success code in else and cleanup in finally, chain with from e, and always keep the traceback with logger.exception(). Read every traceback from the bottom up.

Exceptions ऐसे alarms हैं जो call stack में ऊपर जाते हैं जब तक कोई match करता except जवाब न दे। संकरा और सही क्रम में पकड़ें, try छोटा रखें, सफलता का code else में और cleanup finally में रखें, from e से chain करें, और logger.exception() से हमेशा traceback रखें। हर traceback नीचे से ऊपर पढ़ें।

异常是沿调用栈向上传递的警报,直到有匹配的 except 应答。窄范围、按正确顺序捕获,让 try 保持小巧,成功代码放 else,清理放 finally,用 from e 链接,并始终用 logger.exception() 保留 traceback。每个 traceback 都从下往上读。

🐍 Try Python Yourself

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

You have completed Basic Python Programming. View all sections →