Why Exceptions Exist — The Fire Alarm in Your Program 🚨Exceptions क्यों हैं — आपके Program का Fire Alarm 🚨为什么要有异常 — 程序里的火警 🚨
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.
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。异常处理就是在说:"我知道这里可能失败 — 失败时应该这样做。"
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 — 或者程序停止。
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 的检查中。有了异常,主逻辑保持干净,错误处理放在专门的块里。
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 核对过。
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 才会捕获它。
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 子句里只会写其中几个。下面从根往下逐个介绍。
BaseExceptionKeyboardInterrupt 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() 失效。把它留给框架内部使用。ExceptionBaseException.BaseException को नहीं।BaseException。OSErrorFileNotFoundError, PermissionError and ConnectionError all inherit from it.FileNotFoundError, PermissionError और ConnectionError सभी इससे inherit करते हैं।FileNotFoundError、PermissionError 和 ConnectionError 都继承自它。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…… 当你确切知道会出什么错、如何恢复时,就捕获它们。捕获范围越窄,代码越安全。KeyboardInterruptBaseException, 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 停止你的程序。SystemExitsys.exit(). It also sits directly under BaseException, so a stray except Exception: cannot block a clean shutdown.sys.exit() बुलाता है तब raise होता है। यह भी सीधे BaseException के नीचे है, इसलिए कोई भटका हुआ except Exception: साफ़ shutdown नहीं रोक सकता।sys.exit() 时抛出。它也直接位于 BaseException 之下,所以一个随手写的 except Exception: 挡不住正常退出。KeyboardInterrupt Is Not an ExceptionKeyboardInterrupt एक Exception क्यों नहीं है为什么 KeyboardInterrupt 不是 ExceptionThis 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。)
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.")
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असल में क्या होता है实际发生了什么
try blockPython try block में जाता हैPython 进入 try 块except block and moves past the whole statement.except block छोड़कर पूरे statement के आगे चला जाता है।except 块,越过整个语句。except in orderअगर हाँ — हर except को क्रम से जाँचें如果有 — 按顺序检查每个 excepttry 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)。try/except statement. Nothing is re-raised unless you do it yourself.try/except statement के बाद आगे चलता है। जब तक आप ख़ुद न करें, कुछ फिर से raise नहीं होता।try/except 语句之后继续。除非你自己再次抛出,否则不会重新抛出。except matchesअगर कोई except match न करे如果没有 except 匹配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。
(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,所以程序仍然崩溃。
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
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
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,如果之后还要用,请把它存到另一个名字。
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:。带括号的写法在所有版本都能用,所以共享代码用它更稳妥。
Order Matters — Specific Before General 🪜क्रम मायने रखता है — ख़ास पहले, आम बाद में 🪜顺序很重要 — 先具体后宽泛 🪜
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.जब बड़ा
except छोटे वाले से पहले आता है, ठीक यही होता है। Python पहला match लेता है और बाक़ी को कभी नहीं जाँचता। Python चेतावनी भी नहीं देता।把宽泛的
except 放在具体的之前,发生的正是这种事。Python 选第一个匹配的,从不检查其余的。Python 甚至不会警告你。| CodeCode代码 | What happensक्या होता है会发生什么 |
|---|---|
except OSError | Catches everything I/O relatedहर I/O वाली चीज़ पकड़ता है捕获所有 I/O 相关错误 |
except FileNotFoundError | Dead code — never reachedDead code — कभी नहीं पहुँचता死代码 — 永远到不了 |
| Resultनतीजा结果 | Missing files cannot be handled on their ownग़ायब files को अलग से नहीं संभाल सकते无法单独处理文件缺失 |
| CodeCode代码 | What happensक्या होता है会发生什么 |
|---|---|
except FileNotFoundError | Runs when the file is missingFile ग़ायब होने पर चलता है文件缺失时运行 |
except OSError | Runs 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))
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 各自解决一个实际问题。
try. You can have several.
तभी चलता है जब try के अंदर match करता exception raise हुआ हो। कई हो सकते हैं।
只有当 try 内抛出匹配的异常时才运行。可以有多个。
try finished without raising anything. For code that should run on success but is not itself risky.
तभी चलता है जब try बिना कुछ raise किए ख़त्म हो। उस code के लिए जो सफलता पर चलना चाहिए पर ख़ुद जोखिम वाला नहीं है।
只有当 try 没有 抛出任何异常地完成时才运行。用于成功时才执行、本身没有风险的代码。
return, even when an exception is still flying. Used for cleanup: closing connections, releasing locks.
हमेशा चलता है — सफलता पर, विफलता पर, return के बाद, तब भी जब exception अभी उड़ रहा हो। Cleanup के लिए: connections बंद करना, locks छोड़ना।
总是 运行 — 成功时、失败时、return 之后,甚至异常还在飞行时。用于清理:关闭连接、释放锁。
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"))
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 如何每一次都运行 — 即使错误没有被捕获。
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())
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 只用来做清理。
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}")
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))
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")
| 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 e | A 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 None | Hide 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__}")
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 点的队友)能看清到底缺了哪个文件。
Tracebacks — Following the Trail Back to the Real Error 🕵️Tracebacks — असली Error तक पीछे का रास्ता 🕵️Traceback — 顺着线索找到真正的错误 🕵️
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
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 和代码一起亮起。
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}])
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 打印到 stderr | Quick 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 ---")
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="")
(The timestamp is from the moment this page was built, in UTC.)
(Timestamp उस समय का है जब यह page बना, UTC में।)
(时间戳是本页面构建时的 UTC 时间。)
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。
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अंदाज़ा लगाओ और जाँचो靠猜 |
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完整 traceback | Attached 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}])
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__)
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}")
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 — 它就在同一棵树上。
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)
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"))
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 块几乎零开销;抛出并捕获则有少量开销,所以在非常密集的循环里要避免。
Common Pitfalls — Anti-Patterns Every Codebase Has 🕳️आम गड्ढे — हर Codebase की बुरी आदतें 🕳️常见陷阱 — 每个代码库都有的反模式 🕳️
except:खाली except:裸 except: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 悄无声息。不要这样写。Exception by reflexआदतन Exception पकड़ना习惯性捕获 Exceptionexcept 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)埋在含糊的"出了点问题"之下。只捕获你真正能处理的。except: passचुप except: pass沉默的 except: passlogger.exception().logger.exception() से log करें।logger.exception() 记录一下。str(e) onlyसिर्फ़ str(e) छापना只打印 str(e)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() 保留文件、行号和调用链。trytry में बहुत ज़्यादाtry 里放太多try block catches errors from lines you never meant to guard. Keep try tight — just the risky call — and move the rest to else.try block उन lines के errors भी पकड़ता है जिन्हें आप बचाना नहीं चाहते थे। try छोटा रखें — सिर्फ़ जोखिम वाली call — और बाक़ी else में ले जाएँ।try 块会捕获你从没打算保护的行的错误。让 try 保持紧凑 — 只放风险调用 — 其余移到 else。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 讲出真实经过。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__})")
| UserUser用户 | What happenedक्या हुआ发生了什么 | Which clauseकौन-सा clause哪个子句 |
|---|---|---|
| 1 | Fetched, parsed and savedलाया, parse किया, save किया获取、解析并保存 | else → finally |
| 3 | Server has no such userServer पर ऐसा user नहीं服务器上没有该用户 | except UserNotFound → finally |
| 9 | Network failedNetwork fail हुआ网络失败 | except ConnectionError → finally |
| 2 | Bad 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调用者 |
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 写进日志文件。
Golden Rules and Quick Quiz 🏆सुनहरे नियम और छोटा Quiz 🏆黄金法则与小测验 🏆
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:。FileNotFoundError beats OSError beats Exception.FileNotFoundError बेहतर है OSError से, जो बेहतर है Exception से।FileNotFoundError 胜过 OSError,OSError 胜过 Exception。except; a broad one on top makes the rest dead code.except चलाता है; ऊपर रखा बड़ा वाला बाक़ी को dead code बना देता है।except;宽泛的放在上面会让其余的变成死代码。try blocks tight. Guard the risky call, not fifty lines of logic.try blocks छोटे रखें। जोखिम वाली call बचाएँ, पचास lines का logic नहीं।try 块保持紧凑。保护风险调用,而不是五十行逻辑。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。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。raise X from e when you turn a low-level error into your own error type.raise X from e से chain करें।raise X from e 链接。Exception, with one base class per library so callers can catch broadly or narrowly.Exception से inherit करें, हर library के लिए एक base class के साथ, ताकि callers बड़ा या छोटा पकड़ सकें।Exception,每个库一个基类,让调用者可宽可窄地捕获。dict.get() is often clearer.dict.get() जैसा default अक्सर ज़्यादा साफ़ होता है।dict.get() 这样的默认值往往更清晰。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
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 都从下往上读。