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

Python Functions — Complete Guide

Master Python functions from the ground up: how def works, positional vs keyword arguments, default values, calling defaults using keywords, variable-length *args, keyworded **kwargs, the strict parameter order, and how Python actually passes arguments (pass-by-object-reference). Includes the infamous mutable-default trap, mutable vs immutable behavior, and eight golden rules every Python developer must know.

🌐
SectionSection第 01 节

Why Functions Exist — The Core Idea ☕Functions क्यों होते हैं — मूल विचार ☕为什么需要函数 — 核心思想 ☕

The Coffee Machine on Every Deskहर मेज़ पर कॉफ़ी मशीन每张桌上的咖啡机
Imagine every employee in an office had to grind beans, boil water, measure grams and steam milk each time they wanted coffee. Chaos. Instead, one machine sits in the corner. You press a button and coffee comes out. You don't care how — only that giving it inputs (beans, water, milk) gives the right output.

A function is that coffee machine. Write the logic once, give it a name, and call it whenever you need it. The rest of your code stops caring how it works — only what it returns.
सोचिए कि दफ़्तर के हर कर्मचारी को कॉफ़ी चाहिए तो हर बार बीन्स पीसने, पानी उबालने, ग्राम तौलने और दूध गरम करने पड़ें। अफ़रा-तफ़री। इसकी जगह कोने में एक मशीन रखी है। आप button दबाते हैं और कॉफ़ी निकल आती है। आपको कैसे से मतलब नहीं — बस इतना कि सही inputs (बीन्स, पानी, दूध) देने पर सही output मिले।

Function वही कॉफ़ी मशीन है। Logic एक बार लिखें, उसे नाम दें, और जब भी ज़रूरत हो call करें। बाक़ी code को फ़र्क नहीं पड़ता कि यह कैसे काम करता है — बस यह कि यह क्या लौटाता है।
想象一下,办公室里每个员工想喝咖啡时,都得自己磨豆、烧水、称重、打奶泡。一团糟。取而代之的是角落里的一台机器。按下按钮,咖啡就出来了。你不关心它 怎么 做到的 — 只关心给它输入(豆子、水、牛奶)就能得到正确的输出。

函数 就是那台咖啡机。把逻辑写一次,起个名字,需要时就调用它。其余代码不再关心它如何工作 — 只关心它返回什么。

In Python, a function is a named, reusable block of code. It takes zero or more inputs (called arguments), does some work, and can give back a result with return. Every serious program is built from many small functions that call each other.

Python में function code का एक नाम वाला, दोबारा इस्तेमाल होने वाला हिस्सा है। यह शून्य या ज़्यादा inputs (जिन्हें arguments कहते हैं) लेता है, कुछ काम करता है, और return से result वापस दे सकता है। हर बड़ा program कई छोटे functions से बनता है जो एक-दूसरे को call करते हैं।

在 Python 中,函数 是一段有名字、可复用的代码。它接收零个或多个输入(叫作 实参,arguments),做一些工作,并可以用 return 返回结果。每个正经的程序都由许多互相调用的小函数组成。

A Function Is a Machine: Inputs In, Value OutFunction एक मशीन है: Inputs अंदर, Value बाहर函数是一台机器:输入进去,值出来
arguments go in, a value comes out"Aarav"3Truedef make_coffee(…):grind()boil()pour()return cup☕arguments (inputs)arguments (inputs)实参(输入)the body runsbody चलती है函数体运行return valuereturn value返回值

The caller only sees the arguments it sends and the value it gets back. Everything inside the box stays private.

Caller को सिर्फ़ भेजे गए arguments और वापस मिली value दिखती है। डिब्बे के अंदर का सब कुछ निजी रहता है।

调用者只看到它送进去的实参和拿回来的值。盒子里的一切都是私有的。

🌱
The DRY PrincipleDRY सिद्धांतDRY 原则

Don't Repeat Yourself. If you copy-paste the same three lines into five places, that is a function waiting to be born. Functions make change cheap and bugs rare: fix the logic once, and every caller gets the fix.

Don't Repeat Yourself — ख़ुद को न दोहराएँ। अगर आप वही तीन lines पाँच जगह copy-paste करते हैं, तो वहाँ एक function बनने का इंतज़ार कर रहा है। Functions बदलाव को सस्ता और bugs को कम बनाते हैं: logic एक बार ठीक करें, और हर caller को सुधार मिल जाता है।

Don't Repeat Yourself — 不要重复自己。如果你把同样的三行代码复制粘贴到五个地方,那里就有一个等着诞生的函数。函数让修改变得便宜、bug 变得罕见:逻辑只改一次,每个调用者都得到修复。

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

Look for the interactive boxes: the Function Call Stepper (Section 02), the Argument Matching Machine (06), the Packing Machine for *args/**kwargs (09), the Reference Visualizer (10), the Mutable Default Trap Lab (11) and the Logger Call Builder (12). Change the values, press Play, and watch Python work step by step. Errors match real Python.

Interactive डिब्बे खोजें: Function Call Stepper (Section 02), Argument Matching Machine (06), *args/**kwargs के लिए Packing Machine (09), Reference Visualizer (10), Mutable Default Trap Lab (11) और Logger Call Builder (12)। Values बदलें, Play दबाएँ, और Python को कदम-दर-कदम काम करते देखें। Errors असली Python जैसे ही हैं।

请留意这些互动框:Function Call Stepper(第 02 节)、Argument Matching Machine(06)、用于 *args/**kwargs 的 Packing Machine(09)、Reference Visualizer(10)、Mutable Default Trap Lab(11)和 Logger Call Builder(12)。修改数值,按下播放,一步步看 Python 如何工作。错误信息与真实 Python 一致。


SectionSection第 02 节

The Anatomy of a Function — def, Parameters, return 🔬Function की बनावट — def, Parameters, return 🔬函数的结构 — def、形参、return 🔬

Every Python function has the same skeleton. Learn it once and you can read all of them.

हर Python function का ढाँचा एक जैसा होता है। इसे एक बार सीख लें, तो आप सभी पढ़ सकेंगे।

每个 Python 函数都有相同的骨架。学会一次,就能读懂所有函数。

🛠️ The Four Building Blocksचार बुनियादी हिस्से四个组成部分
def
The keyword that tells Python "a function definition starts here". The name comes next. वह keyword जो Python को बताता है "यहाँ से function की definition शुरू होती है"। उसके बाद नाम आता है। 告诉 Python "函数定义从这里开始" 的 关键字。后面跟着函数名。
name
The identifier you use to call it later. By convention, use snake_case. वह नाम जिससे आप बाद में इसे call करते हैं। परंपरा से snake_case इस्तेमाल करें। 之后用来调用它的 标识符。按惯例使用 snake_case。
params
The parameters in brackets — the inputs the function expects. Can be empty. Brackets में parameters — function जिन inputs की उम्मीद करता है। ख़ाली भी हो सकते हैं। 括号里的 形参 — 函数期望的输入。可以为空。
return
The value the function hands back. Optional — if missing, Python returns None. वह value जो function वापस देता है। वैकल्पिक — न हो तो Python None लौटाता है। 函数交回的 值。可选 — 没有时 Python 返回 None。

Minimal Example — Greeting a Userसबसे छोटा उदाहरण — User का स्वागत最小示例 — 问候用户

def greet(name):
    # Body indented with 4 spaces
    message = f"Hello, {name}!"
    return message

# Calling the function
result = greet("Aarav")
print(result)
OUTPUT
Hello, Aarav!
💡
Parameters vs Arguments — Know the DifferenceParameters बनाम Arguments — अंतर जानें形参与实参 — 分清区别

A parameter is the variable name in the function definition (name above). An argument is the actual value you pass when calling it ("Aarav"). People mix these up in casual talk, but interviewers love this difference.

Parameter function की definition में लिखा variable का नाम है (ऊपर name)। Argument वह असली value है जो call करते समय दी जाती है ("Aarav")। आम बातचीत में लोग इन्हें मिला देते हैं, पर interview लेने वालों को यह अंतर बहुत पसंद है।

形参(parameter)是函数定义里的变量名(上面的 name)。实参(argument)是调用时传入的实际值("Aarav")。日常交流中人们常混用,但面试官很爱考这个区别。

Functions Without return — What Actually Happensreturn के बिना Functions — असल में क्या होता है没有 return 的函数 — 实际发生了什么

def shout(word):
    print(word.upper() + "!")
    # No return statement

result = shout("hello")
print("result is:", result)
print("type is:", type(result))
OUTPUT
HELLO! result is: None type is: <class 'NoneType'>
⚠️
Printing Is Not ReturningPrint करना, Return करना नहीं है打印不等于返回

print shows something on the screen. return hands a value back to the caller so it can be stored, passed on or used. A function that only prints cannot be chained or tested.

print screen पर कुछ दिखाता है। return caller को value वापस देता है ताकि उसे रखा, आगे भेजा या इस्तेमाल किया जा सके। जो function सिर्फ़ print करता है, उसे न जोड़ा जा सकता है, न test किया जा सकता है।

print 在屏幕上显示内容。return 把值交还给调用者,让它可以被保存、传递或使用。只会 print 的函数无法串联使用,也无法测试。

Returning Several Valuesकई Values लौटाना返回多个值

A function can return only one object — but that object can be a tuple. Write values with commas after return, and unpack them at the call site.

Function सिर्फ़ एक object लौटा सकता है — पर वह object एक tuple हो सकता है। return के बाद values को commas से लिखें, और call करने की जगह उन्हें अलग-अलग नामों में खोल लें।

函数只能返回一个对象 — 但这个对象可以是 tuple。在 return 后用逗号写多个值,再在调用处解包。

def min_max(numbers):
    return min(numbers), max(numbers)     # really ONE tuple

pair = min_max([4, 9, 1, 7])
print(pair, type(pair).__name__)

low, high = min_max([4, 9, 1, 7])        # unpack it into two names
print("low =", low, "| high =", high)
OUTPUT
(1, 9) tuple low = 1 | high = 9

Try It — Function Call Stepperकरके देखें — Function Call Stepper动手试试 — 函数调用步进器

Watch what really happens when you call a function. A new frame (a private workspace for the function's variables) is pushed onto the call stack, the body runs, and return hands a value back before the frame disappears. Try the print vs return tab to see why printing is not returning.

देखें कि function call करने पर असल में क्या होता है। Call stack पर एक नया frame (function के variables का निजी कार्यस्थान) रखा जाता है, body चलती है, और frame के गायब होने से पहले return value वापस देता है। print vs return tab आज़माएँ और देखें कि print करना return करना क्यों नहीं है।

看看调用函数时究竟发生了什么。一个新的 帧(存放函数变量的私有工作区)被压入 调用栈,函数体运行,return 在帧消失之前把值交回。试试 print vs return 标签页,看看为什么打印不等于返回。

🧾 Function Call StepperCall → new frame → return → frame removed.Call → नया frame → return → frame हटा।调用 → 新帧 → 返回 → 帧移除。
screen outputscreen output屏幕输出
call stack (top = running now)call stack (ऊपर = अभी चल रहा)调用栈(顶部 = 正在运行)

SectionSection第 03 节

Positional Arguments — Order Is Everything 🔢Positional Arguments — क्रम ही सब कुछ है 🔢位置参数 — 顺序就是一切 🔢

When you call a function with values but no names, Python matches them by position — first argument to first parameter, second to second, and so on. Swap the order and you get a bug, not an error.

जब आप बिना नामों के values देकर function call करते हैं, तो Python उन्हें स्थान से मिलाता है — पहला argument पहले parameter को, दूसरा दूसरे को, और आगे भी ऐसे ही। क्रम उलट दें तो error नहीं, bug मिलता है।

当你只传值、不写名字调用函数时,Python 按位置 匹配 — 第一个实参给第一个形参,第二个给第二个,依此类推。顺序一换,得到的是 bug,而不是报错。

def divide(numerator, denominator):
    return numerator / denominator

# Positional — order matters
print(divide(10, 2))   # 10 / 2 = 5.0   correct
print(divide(2, 10))   # 2 / 10 = 0.2   silent bug!
OUTPUT
5.0 0.2
🚩
The Silent Bug Problemचुपचाप Bug की समस्या隐蔽 bug 的问题

Python does not check whether your values make sense — only that the count matches. Wrong-order positional arguments give wrong answers with zero warnings. This is why keyword arguments exist.

Python यह नहीं जाँचता कि आपकी values का कोई मतलब बनता है या नहीं — सिर्फ़ यह कि गिनती मेल खाती है। ग़लत क्रम के positional arguments बिना किसी चेतावनी के ग़लत जवाब देते हैं। इसीलिए keyword arguments बने हैं।

Python 不检查你的值 是否合理 — 只检查数量是否对得上。顺序错了的位置参数会在没有任何警告的情况下给出错误答案。这就是 关键字参数 存在的原因。


SectionSection第 04 节

Keyword Arguments — Name Them and Sleep Well 🏷️Keyword Arguments — नाम दें और चैन से सोएँ 🏷️关键字参数 — 写上名字,睡个好觉 🏷️

You can pass arguments by name instead of position with the parameter=value syntax. Keyword arguments explain themselves and are safe from the order-swap bug above.

parameter=value लिखकर आप arguments को स्थान की जगह नाम से दे सकते हैं। Keyword arguments ख़ुद अपना मतलब बताते हैं और ऊपर वाले क्रम-उलटने के bug से सुरक्षित हैं।

用 parameter=value 语法,可以 按名字 而不是按位置传参。关键字参数自带说明,也不会出现上面那种顺序颠倒的 bug。

def divide(numerator, denominator):
    return numerator / denominator

# Keyword calls — order does not matter
print(divide(numerator=10, denominator=2))   # 5.0
print(divide(denominator=2, numerator=10))   # 5.0 — same!

# Mix positional then keyword (positional MUST come first)
print(divide(10, denominator=2))          # 5.0  OK

# Keyword before positional is a SyntaxError — Python refuses to run the file
try:
    compile("divide(numerator=10, 2)", "<test>", "eval")
except SyntaxError as e:
    print("SyntaxError:", e.msg)
OUTPUT
5.0 5.0 5.0 SyntaxError: positional argument follows keyword argument
✅
The Golden Rule of Clear Callsसाफ़ Calls का सुनहरा नियम清晰调用的黄金法则

If a function takes more than two arguments, or any argument is a True/False flag, use keyword arguments at the call site. Compare send_email("a@b", "hi", True, False) with send_email(to="a@b", subject="hi", html=True, urgent=False). Only the second is readable six months later.

अगर function दो से ज़्यादा arguments लेता है, या कोई argument True/False flag है, तो call करते समय keyword arguments इस्तेमाल करें। send_email("a@b", "hi", True, False) की तुलना send_email(to="a@b", subject="hi", html=True, urgent=False) से करें। छह महीने बाद सिर्फ़ दूसरा पढ़ा जा सकेगा।

如果函数接收超过 两个 参数,或者有 True/False 开关参数,调用时就用关键字参数。比较 send_email("a@b", "hi", True, False) 和 send_email(to="a@b", subject="hi", html=True, urgent=False)。六个月后只有第二个还能看懂。


SectionSection第 05 节

Default Arguments — Sensible Fallbacks 🎛️Default Arguments — समझदार विकल्प 🎛️默认参数 — 合理的后备值 🎛️

You can give a parameter a default value in the definition with parameter=default. If the caller does not supply it, Python uses the default. This makes functions flexible without forcing callers to set every option.

Definition में parameter=default लिखकर आप parameter को default value दे सकते हैं। Caller अगर उसे न दे, तो Python default इस्तेमाल करता है। इससे functions लचीले बनते हैं और caller को हर विकल्प भरना नहीं पड़ता।

在定义中用 parameter=default 给形参一个 默认值。调用者不提供时,Python 就用默认值。这让函数更灵活,调用者不必设置每个选项。

def connect(host, port=8080, timeout=30, use_ssl=True):
    print(f"Connecting to {host}:{port}")
    print(f"  timeout={timeout}s, ssl={use_ssl}")

# Only the required argument — rest use defaults
connect("api.example.com")

# Override just the port
connect("api.example.com", 9000)

# Override port and timeout, keep ssl=True
connect("api.example.com", 9000, 60)
OUTPUT
Connecting to api.example.com:8080 timeout=30s, ssl=True Connecting to api.example.com:9000 timeout=30s, ssl=True Connecting to api.example.com:9000 timeout=60s, ssl=True
📌 Rules for Default ArgumentsDefault Arguments के नियम默认参数的规则
Rule 1नियम 1规则 1
Parameters with defaults must come after the ones without. def f(a=1, b) is a SyntaxError: parameter without a default follows parameter with a default. Default वाले parameters, बिना default वालों के बाद आने चाहिए। def f(a=1, b) एक SyntaxError है: parameter without a default follows parameter with a default। 带默认值的形参必须写在不带默认值的形参 之后。def f(a=1, b) 是 SyntaxError:parameter without a default follows parameter with a default。
Rule 2नियम 2规则 2
Defaults are evaluated once, when the def line runs — not on every call. This causes the famous mutable-default trap (Section 11). Defaults एक बार बनते हैं, जब def line चलती है — हर call पर नहीं। इसी से मशहूर mutable-default जाल बनता है (Section 11)। 默认值只在 def 这一行运行时计算 一次 — 而不是每次调用都算。这就是著名的可变默认值陷阱的来源(第 11 节)。
Rule 3नियम 3规则 3
Prefer unchangeable defaults: numbers, strings, tuples, None. Never [] or {} directly. न बदलने वाले defaults चुनें: numbers, strings, tuples, None। सीधे [] या {} कभी नहीं। 优先使用 不可变 的默认值:数字、字符串、tuple、None。绝不要直接用 [] 或 {}。

SectionSection第 06 节

Calling Default Arguments by Keyword — The Skip Trick ⏭️Default Arguments को Keyword से Call करना — छोड़ने की तरकीब ⏭️用关键字调用默认参数 — 跳过技巧 ⏭️

Here defaults and keyword calls combine into a superpower. What if you want to keep the defaults for port and timeout, but change only use_ssl? Without keywords you would have to repeat every earlier value. With them, you jump straight to the one you care about.

यहाँ defaults और keyword calls मिलकर ख़ास ताक़त बनते हैं। अगर आप port और timeout के defaults रखना चाहें, पर सिर्फ़ use_ssl बदलना चाहें तो? Keywords के बिना आपको पहले की हर value दोहरानी पड़ती। उनके साथ आप सीधे उसी पर पहुँचते हैं जो आपको चाहिए।

在这里,默认值和关键字调用合在一起成了超能力。如果你想保留 port 和 timeout 的默认值,只改 use_ssl 呢?没有关键字,你得把前面的每个值都重写一遍。有了关键字,你可以直接跳到你关心的那一个。

🚫 The Painful Way (positional only)तकलीफ़ वाला तरीका (सिर्फ़ positional)痛苦的方式(只用位置参数)
CallCall调用What Happenedक्या हुआ结果
connect("h", 8080, 30, False)Had to repeat every defaultहर default दोहराना पड़ा不得不重复每个默认值
Fragileनाज़ुक脆弱Change one default → every call site is now out of dateएक default बदला → हर call पुरानी हो गई改一个默认值 → 每个调用处都过时了
Unreadableपढ़ने में कठिन难以阅读What does the False mean?False का मतलब क्या है?False 是什么意思?
✅ The Clean Way (keyword override)साफ़ तरीका (keyword से बदलना)干净的方式(关键字覆盖)
CallCall调用What Happenedक्या हुआ结果
connect("h", use_ssl=False)Only override what changesसिर्फ़ जो बदले, वही बदलें只覆盖需要改的
Robustमज़बूत稳健New optional params at the end never break callersअंत में जुड़े नए वैकल्पिक params callers को नहीं तोड़ते在末尾新增可选参数不会破坏调用者
Self-documentingख़ुद समझाने वाला自带说明The False has a nameFalse का एक नाम हैFalse 有了名字
def connect(host, port=8080, timeout=30, use_ssl=True):
    print(f"{host}:{port} ssl={use_ssl} t={timeout}")

# Keep port & timeout defaults, only change ssl
connect("api.example.com", use_ssl=False)

# Skip port, change timeout only
connect("api.example.com", timeout=60)

# Any combination — order does not matter
connect("api.example.com", use_ssl=False, timeout=60, port=443)
OUTPUT
api.example.com:8080 ssl=False t=30 api.example.com:8080 ssl=True t=60 api.example.com:443 ssl=False t=60
🔑
Why This Matters in Real Librariesअसली Libraries में यह क्यों ज़रूरी है为什么这在真实的库中很重要

Look at almost any function in pandas, requests or sklearn — many take 10 to 30 parameters, nearly all with defaults. You never pass them by position. You override two or three by name and leave the rest at their defaults. This pattern makes big APIs usable.

pandas, requests या sklearn का लगभग कोई भी function देखें — कई 10 से 30 parameters लेते हैं, लगभग सभी defaults के साथ। आप उन्हें कभी स्थान से नहीं देते। आप दो-तीन को नाम से बदलते हैं और बाक़ी defaults पर छोड़ देते हैं। यही तरीका बड़े APIs को इस्तेमाल लायक बनाता है।

看看 pandas、requests 或 sklearn 里几乎任何函数 — 很多都有 10 到 30 个形参,几乎全都有默认值。你从不按位置传它们,而是按名字覆盖两三个,其余保持默认。正是这种模式让庞大的 API 变得好用。

Try It — Argument Matching Machineकरके देखें — Argument Matching Machine动手试试 — 参数匹配机

Edit the def line or the call, then press Call it. Each argument you pass is a card; each parameter is a slot. Watch positional cards fill slots in order, keyword cards jump to the slot with the same name, and defaults fill whatever is left. Try the ✗ chips to see every error Python can raise.

def line या call बदलें, फिर Call करें दबाएँ। हर argument एक card है; हर parameter एक slot। देखें कि positional cards slots को क्रम से भरते हैं, keyword cards उसी नाम वाले slot पर कूदते हैं, और बचा हुआ defaults भरते हैं। Python के हर error को देखने के लिए ✗ वाले chips आज़माएँ।

修改 def 行或调用,然后按 调用。你传的每个实参是一张卡片,每个形参是一个槽。看位置卡片 按顺序 填槽,关键字卡片跳到 同名 的槽,剩下的由默认值填充。试试带 ✗ 的按钮,看看 Python 会抛出的每种错误。

🧩 Argument Matching MachinePositional → by order. Keyword → by name. Missing → default.Positional → क्रम से। Keyword → नाम से। ग़ायब → default।位置 → 按顺序。关键字 → 按名字。缺失 → 默认值。
Try:आज़माएँ:试试:
arguments you pass (call site)आपके दिए arguments (call में)你传入的实参(调用处)
▼ matched intoइनमें मिलाए गए匹配到 ▼
parameter slots (from the def)parameter slots (def से)形参槽(来自 def)

SectionSection第 07 节

Variable-Length Arguments — *args 🎒बदलती संख्या के Arguments — *args 🎒可变长度参数 — *args 🎒

The Bag That Grows to Fitज़रूरत के हिसाब से बढ़ने वाला थैला能随需求变大的袋子
You are writing a sum_all function. Some callers pass 2 numbers. Some pass 5. Some pass 200. You cannot list 200 parameters. You need a bag that catches "however many numbers they gave me" and lets you loop through them. That bag is *args.
आप एक sum_all function लिख रहे हैं। कुछ callers 2 numbers देते हैं, कुछ 5, कुछ 200। आप 200 parameters नहीं लिख सकते। आपको एक थैला चाहिए जो "जितने भी numbers दिए गए" उन्हें पकड़ ले और आपको उन पर loop चलाने दे। वह थैला *args है।
你在写一个 sum_all 函数。有的调用者传 2 个数,有的传 5 个,有的传 200 个。你不可能列出 200 个形参。你需要一个袋子,接住 "他们给我的所有数字",让你可以遍历它们。这个袋子就是 *args。

A single asterisk * before a parameter tells Python: "catch all remaining positional arguments and pack them into a tuple". The name args is only a convention — *numbers works too. The magic is the *.

Parameter से पहले एक asterisk * Python को बताता है: "बचे हुए सारे positional arguments पकड़ो और उन्हें tuple में पैक करो"। नाम args सिर्फ़ परंपरा है — *numbers भी चलता है। असली जादू * में है।

形参前加一个星号 * 是在告诉 Python:"接住所有剩下的 位置 参数,打包成 tuple"。名字 args 只是惯例 — *numbers 也可以。真正的魔法在 *。

def sum_all(*args):
    print("args is:", args)
    print("type is:", type(args))
    total = 0
    for n in args:
        total += n
    return total

print(sum_all(1, 2, 3))
print(sum_all(10, 20, 30, 40, 50))
print(sum_all())          # zero args is fine — empty tuple
OUTPUT
args is: (1, 2, 3) type is: <class 'tuple'> 6 args is: (10, 20, 30, 40, 50) type is: <class 'tuple'> 150 args is: () type is: <class 'tuple'> 0

Mixing Regular Parameters with *argsसामान्य Parameters के साथ *args普通形参与 *args 混用

def log_event(level, *messages):
    for msg in messages:
        print(f"[{level}] {msg}")

log_event("INFO", "server started", "port 8080 open", "ready")
OUTPUT
[INFO] server started [INFO] port 8080 open [INFO] ready

Unpacking — the Other Side of * and **Unpacking — * और ** का दूसरा पहलू解包 — * 和 ** 的另一面

The same stars work at the call site in reverse. * spreads a list or tuple into positional arguments, and ** spreads a dict into keyword arguments. Defining and calling are mirror images.

वही stars call की जगह उल्टा काम करते हैं। * list या tuple को positional arguments में फैला देता है, और ** dict को keyword arguments में। Define करना और call करना एक-दूसरे का प्रतिबिंब हैं।

同样的星号在 调用处 反向工作。* 把 list 或 tuple 展开成位置参数,** 把 dict 展开成关键字参数。定义和调用互为镜像。

def describe(name, age, city):
    print(f"{name}, {age}, from {city}")

values = ["Meera", 31, "Pune"]
describe(*values)                 # * unpacks a list into positions

options = {"city": "Delhi", "age": 25, "name": "Kabir"}
describe(**options)               # ** unpacks a dict into keywords

print(*[1, 2, 3], sep=" | ")      # works with built-ins too
OUTPUT
Meera, 31, from Pune Kabir, 25, from Delhi 1 | 2 | 3

SectionSection第 08 节

Keyword Arguments Bag — **kwargs 🗃️Keyword Arguments का थैला — **kwargs 🗃️关键字参数袋 — **kwargs 🗃️

A double asterisk ** is the cousin of *args. It catches all remaining keyword arguments and packs them into a dictionary. Your function can then accept an open-ended set of named options without declaring each one.

दोहरा asterisk **, *args का भाई है। यह बचे हुए सारे keyword arguments पकड़कर एक dictionary में पैक करता है। तब आपका function हर एक को घोषित किए बिना नाम वाले विकल्पों का खुला सेट ले सकता है।

双星号 ** 是 *args 的兄弟。它接住所有剩下的 关键字 参数,打包成 字典。这样你的函数不必逐个声明,也能接受任意多个具名选项。

def create_user(username, **kwargs):
    print(f"Creating user: {username}")
    print("kwargs is:", kwargs)
    print("type is: ", type(kwargs))
    for key, value in kwargs.items():
        print(f"  {key} = {value}")

create_user(
    "priya_92",
    email="priya@example.com",
    age=28,
    country="India",
    is_admin=False
)
OUTPUT
Creating user: priya_92 kwargs is: {'email': 'priya@example.com', 'age': 28, 'country': 'India', 'is_admin': False} type is: <class 'dict'> email = priya@example.com age = 28 country = India is_admin = False
Featureविशेषता特点*args**kwargs
Catchesपकड़ता है接收Extra positional argumentsअतिरिक्त positional arguments多余的 位置 参数Extra keyword argumentsअतिरिक्त keyword arguments多余的 关键字 参数
Packed intoकिसमें पैक打包成tupledict
Common nameआम नाम常用名args (convention)args (परंपरा)args(惯例)kwargs (convention)kwargs (परंपरा)kwargs(惯例)
Access patternपढ़ने का तरीका访问方式for x in args:for k, v in kwargs.items():
Place in defdef में जगह在 def 中的位置Before keyword-only params and **kwargsKeyword-only params और **kwargs से पहले在仅关键字形参和 **kwargs 之前Must be the last parameterआख़िरी parameter होना ज़रूरी必须是 最后一个 形参

SectionSection第 09 节

The Full Parameter Order — Putting It All Together 🧱Parameters का पूरा क्रम — सब एक साथ 🧱完整的形参顺序 — 全部组合起来 🧱

When a function uses every kind of parameter at once, Python enforces a strict order in the definition. Here is the full sequence. Every part is optional, but the parts you use must appear in this order.

जब कोई function हर तरह के parameter एक साथ इस्तेमाल करता है, तो Python definition में सख़्त क्रम लागू करता है। यह रहा पूरा क्रम। हर हिस्सा वैकल्पिक है, पर जो हिस्से आप इस्तेमाल करें, वे इसी क्रम में आने चाहिए।

当一个函数同时使用各种形参时,Python 在定义中强制要求严格的顺序。下面是完整的顺序。每一部分都是可选的,但用到的部分必须按这个顺序出现。

01
Positional-only parameters, then /सिर्फ़-positional parameters, फिर /仅位置形参,然后 /
Optional (Python 3.8+). Callers can fill these only by position, never by name. Common in built-ins such as len(obj, /).
वैकल्पिक (Python 3.8+)। Callers इन्हें सिर्फ़ स्थान से भर सकते हैं, नाम से कभी नहीं। len(obj, /) जैसे built-ins में आम।
可选(Python 3.8+)。调用者只能按位置填写,不能按名字。常见于内置函数,如 len(obj, /)。
02
Regular parametersसामान्य parameters普通形参
The required inputs with no defaults. Callers must supply these, by position or by name.
बिना default वाले ज़रूरी inputs। Callers को ये देने ही होंगे, स्थान से या नाम से।
没有默认值的必需输入。调用者必须提供,按位置或按名字都行。
03
Parameters with default valuesDefault values वाले parameters带默认值的形参
Optional inputs. Callers can override them by position or by keyword.
वैकल्पिक inputs। Callers इन्हें स्थान से या keyword से बदल सकते हैं।
可选输入。调用者可以按位置或按关键字覆盖。
04
*args (or a bare *)*args (या अकेला *)*args(或单独的 *)
Catches any extra positional arguments. A bare * catches nothing — it only marks where keyword-only parameters begin.
कोई भी अतिरिक्त positional arguments पकड़ता है। अकेला * कुछ नहीं पकड़ता — वह सिर्फ़ बताता है कि keyword-only parameters कहाँ से शुरू होते हैं।
接收所有多余的位置参数。单独的 * 什么也不接收 — 它只标记仅关键字形参从哪里开始。
05
Keyword-only parametersसिर्फ़-keyword parameters仅关键字形参
Anything after *args or *. Callers must pass these by name. They may have defaults or not, in any order.
*args या * के बाद का सब कुछ। Callers को इन्हें नाम से देना होगा। इनके defaults हो भी सकते हैं और नहीं भी, किसी भी क्रम में।
写在 *args 或 * 之后的形参。调用者必须按名字传入。它们可以有默认值也可以没有,顺序随意。
06
**kwargs — variable keyword**kwargs — बदलते keywords**kwargs — 可变关键字
Catches any extra keyword arguments. Always the very last parameter.
कोई भी अतिरिक्त keyword arguments पकड़ता है। हमेशा सबसे आख़िरी parameter।
接收所有多余的关键字参数。永远是最后一个形参。
def register(username, email, role="user", *permissions, **profile):
    print(f"username    : {username}")
    print(f"email       : {email}")
    print(f"role        : {role}")
    print(f"permissions : {permissions}")
    print(f"profile     : {profile}")

register(
    "rahul_dev",                     # username (positional)
    "rahul@example.com",              # email    (positional)
    "admin",                         # role     (overrides default)
    "read", "write", "delete",        # *permissions -> tuple
    country="India",                  # **profile -> dict
    joined="2024-01-15"
)
OUTPUT
username : rahul_dev email : rahul@example.com role : admin permissions : ('read', 'write', 'delete') profile : {'country': 'India', 'joined': '2024-01-15'}

Keyword-Only and Positional-Only Parametersसिर्फ़-Keyword और सिर्फ़-Positional Parameters仅关键字与仅位置形参

Two markers give you extra control. Anything after *args (or a bare *) must be passed by name. Anything before / must be passed by position. The logger in Section 12 uses the first trick for level.

दो निशान आपको अतिरिक्त नियंत्रण देते हैं। *args (या अकेले *) के बाद का सब कुछ नाम से देना होगा। / से पहले का सब कुछ स्थान से देना होगा। Section 12 का logger level के लिए पहला तरीका इस्तेमाल करता है।

两个标记给你更多控制。*args(或单独的 *)之后的形参必须 按名字 传入。/ 之前的形参必须 按位置 传入。第 12 节的日志函数就用第一种技巧来处理 level。

# Parameters AFTER *args (or a bare *) are keyword-only
def order(item, *extras, size="regular"):
    print(f"{size} {item} with {extras}")

order("tea", "sugar", "milk")              # size keeps its default
order("tea", "sugar", size="large")         # size only by name

# Parameters BEFORE / are positional-only (Python 3.8+)
def power(base, exp, /, *, verbose=False):
    result = base ** exp
    if verbose:
        print(f"{base} ** {exp} = {result}")
    return result

print(power(2, 10))
power(2, 10, verbose=True)
try:
    power(base=2, exp=10)
except TypeError as e:
    print("TypeError:", e)
OUTPUT
regular tea with ('sugar', 'milk') large tea with ('sugar',) 1024 2 ** 10 = 1024 TypeError: power() got some positional-only arguments passed as keyword arguments: 'base, exp'
⚠️
Break the Order → SyntaxErrorक्रम तोड़ा → SyntaxError打乱顺序 → SyntaxError

Write def f(**kw, x) and Python refuses to even read the file: arguments cannot follow var-keyword argument. Two stars (def f(*a, *b)) fail too. But def f(*args, x, y) is valid — x and y simply become keyword-only. The order is not a style guide — it is grammar.

def f(**kw, x) लिखें तो Python file पढ़ने से ही मना कर देता है: arguments cannot follow var-keyword argument। दो stars (def f(*a, *b)) भी विफल होते हैं। पर def f(*args, x, y) सही है — x और y बस keyword-only बन जाते हैं। यह क्रम style का सुझाव नहीं — व्याकरण है।

写 def f(**kw, x),Python 连文件都不肯读:arguments cannot follow var-keyword argument。两个星号(def f(*a, *b))同样失败。但 def f(*args, x, y) 是 合法 的 — x 和 y 只是变成了仅关键字形参。这个顺序不是风格建议 — 而是语法。

Try It — Packing Machineकरके देखें — Packing Machine动手试试 — 打包机

Same machine, now with bags. Arguments that find no named slot drop into the *args bag (a tuple) or the **kwargs bag (a dict). A * or ** at the call site does the opposite: it unpacks a list or dict into separate arguments. Try the register chips to see all four kinds at once.

वही मशीन, अब थैलों के साथ। जिन arguments को कोई नाम वाला slot नहीं मिलता, वे *args थैले (एक tuple) या **kwargs थैले (एक dict) में गिरते हैं। Call में * या ** उल्टा करता है: list या dict को अलग-अलग arguments में खोल देता है। चारों तरह एक साथ देखने के लिए register वाले chips आज़माएँ।

同一台机器,现在多了袋子。找不到具名槽的实参会掉进 *args 袋(一个 tuple)或 **kwargs 袋(一个 dict)。调用 处的 * 或 ** 正好相反:把 list 或 dict 拆成单独的实参。试试 register 按钮,一次看到全部四种。

🎒 Packing MachineExtra positional → *args tuple. Extra keyword → **kwargs dict.अतिरिक्त positional → *args tuple। अतिरिक्त keyword → **kwargs dict।多余的位置参数 → *args tuple。多余的关键字参数 → **kwargs dict。
Try:आज़माएँ:试试:
arguments you pass (call site)आपके दिए arguments (call में)你传入的实参(调用处)
▼ matched intoइनमें मिलाए गए匹配到 ▼
parameter slots (from the def)parameter slots (def से)形参槽(来自 def)

SectionSection第 10 节

How Arguments Are Passed — Object References 🔗Arguments कैसे दिए जाते हैं — Object References 🔗参数如何传递 — 对象引用 🔗

Sharing a Google Doc vs Emailing a PDFGoogle Doc share करना बनाम PDF email करना共享 Google 文档与发送 PDF 邮件
When you email a PDF of a report, the reader's edits don't affect your copy — they got a snapshot. When you share a Google Doc link, you both look at the same file — one person's edits are visible to the other at once.

Python does neither pure "pass by value" nor pure "pass by reference". It does what is often called pass-by-object-reference (or "pass by assignment"). The name inside the function points to the same object the caller passed. Whether changes leak out depends on what you do with it — and whether the object can change at all.
जब आप किसी report की PDF email करते हैं, तो पढ़ने वाले के बदलाव आपकी copy पर असर नहीं डालते — उसे एक snapshot मिला। जब आप Google Doc का link share करते हैं, तो दोनों एक ही file देखते हैं — एक के बदलाव दूसरे को तुरंत दिखते हैं।

Python न पूरी तरह "pass by value" करता है, न पूरी तरह "pass by reference"। यह वह करता है जिसे अक्सर pass-by-object-reference (या "pass by assignment") कहते हैं। Function के अंदर का नाम उसी object की ओर इशारा करता है जो caller ने दिया। बदलाव बाहर दिखेंगे या नहीं, यह इस पर निर्भर है कि आप उसके साथ क्या करते हैं — और क्या वह object बदल भी सकता है।
当你 用邮件发送 报告的 PDF 时,对方的修改不会影响你的副本 — 他拿到的是快照。当你 分享 Google 文档链接 时,你们看的是同一个文件 — 一个人的修改另一个人立刻能看到。

Python 既不是纯粹的 "值传递",也不是纯粹的 "引用传递"。它采用的方式通常叫 对象引用传递(pass-by-object-reference,也叫 "赋值传递")。函数里的名字指向调用者传入的 同一个对象。修改会不会泄漏出去,取决于你怎么使用它 — 以及这个对象究竟能不能被修改。
🔒
The One Rule to Rememberयाद रखने वाला एक नियम要记住的唯一规则

Python passes a reference to the object, not a copy of it. What happens next depends on whether you mutate that object in place, or rebind the local name to a new object.

Python object का reference देता है, उसकी copy नहीं। आगे क्या होगा, यह इस पर निर्भर है कि आप उस object को उसी जगह बदलते (mutate) हैं, या local नाम को किसी नए object से जोड़ते (rebind) हैं।

Python 传递的是对象的 引用,而不是副本。接下来会发生什么,取决于你是 原地修改(mutate)这个对象,还是把局部名字 重新绑定(rebind)到新对象上。

Case 1 — Unchangeable Objects (int, str, tuple, float, bool)मामला 1 — न बदलने वाले Objects (int, str, tuple, float, bool)情况 1 — 不可变对象(int、str、tuple、float、bool)

def try_change(x):
    print(f"  inside before: x = {x}, id = {id(x)}")
    x = x + 100                     # rebinds LOCAL x to a new int
    print(f"  inside after : x = {x}, id = {id(x)}")

number = 5
print(f"before call: number = {number}, id = {id(number)}")
try_change(number)
print(f"after call : number = {number}, id = {id(number)}")
OUTPUT
before call: number = 5, id = 10458792 inside before: x = 5, id = 10458792 inside after : x = 105, id = 10461992 after call : number = 5, id = 10458792

Look at the ids. The parameter x started out pointing at the very same integer as number. But x = x + 100 created a new integer and moved the local name x to it. The caller's number was never touched, because integers cannot change — you cannot modify 5 in place, only create a new object. (Your id numbers will be different; only which ones match matters.)

Ids देखें। Parameter x शुरू में बिल्कुल उसी integer की ओर इशारा करता था जिसकी ओर number। पर x = x + 100 ने एक नया integer बनाया और local नाम x को उस पर ले गया। Caller का number कभी छुआ नहीं गया, क्योंकि integers बदल नहीं सकते — आप 5 को उसी जगह नहीं बदल सकते, सिर्फ़ नया object बना सकते हैं। (आपके id numbers अलग होंगे; मायने सिर्फ़ यह रखता है कि कौन-से मेल खाते हैं।)

看看这些 id。形参 x 一开始指向的就是 number 指向的那个整数。但 x = x + 100 创建了一个 新 整数,并把局部名字 x 移了过去。调用者的 number 从未被碰过,因为整数不可变 — 你不能原地修改 5,只能创建新对象。(你的 id 数值会不同;重要的只是哪些相同。)

Case 2 — Changeable Objects (list, dict, set, your own classes)मामला 2 — बदलने वाले Objects (list, dict, set, आपकी अपनी classes)情况 2 — 可变对象(list、dict、set、自定义类)

def add_item(cart):
    print(f"  inside before: cart = {cart}, id = {id(cart)}")
    cart.append("laptop")          # MUTATES the object in place
    print(f"  inside after : cart = {cart}, id = {id(cart)}")

my_cart = ["book", "pen"]
print(f"before call: cart = {my_cart}, id = {id(my_cart)}")
add_item(my_cart)
print(f"after call : cart = {my_cart}, id = {id(my_cart)}")
OUTPUT
before call: cart = ['book', 'pen'], id = 140278184837056 inside before: cart = ['book', 'pen'], id = 140278184837056 inside after : cart = ['book', 'pen', 'laptop'], id = 140278184837056 after call : cart = ['book', 'pen', 'laptop'], id = 140278184837056
🚩
This Is Where Beginners Get Burnedयहीं नए सीखने वाले फँसते हैं初学者最容易在这里吃亏

A function that takes a list and calls .append() on it quietly changes the caller's list. There is no warning, no return, no signal — the change just happens. Two weeks later someone cannot work out why their list has extra items.

जो function list लेकर उस पर .append() चलाता है, वह चुपचाप caller की list बदल देता है। न चेतावनी, न return, न कोई संकेत — बदलाव बस हो जाता है। दो हफ़्ते बाद कोई समझ नहीं पाता कि उसकी list में अतिरिक्त items क्यों हैं।

一个接收 list 并对它调用 .append() 的函数,会悄悄修改调用者的 list。没有警告,没有返回值,没有任何信号 — 修改就这么发生了。两周后,有人怎么也想不明白自己的 list 里为什么多了东西。

Case 3 — Changeable Object, but Rebinding Instead of Mutatingमामला 3 — बदलने वाला Object, पर बदलने की जगह Rebinding情况 3 — 可变对象,但重新绑定而不是修改

def replace_cart(cart):
    cart = ["phone", "charger"]     # REBINDS local name to a NEW list
    print(f"  inside: cart = {cart}")

my_cart = ["book", "pen"]
replace_cart(my_cart)
print(f"after : cart = {my_cart}")     # unchanged!
OUTPUT
inside: cart = ['phone', 'charger'] after : cart = ['book', 'pen']

Same type (a list, which can change), but a completely different result. The assignment cart = [...] did not modify the caller's list — it only made the local name cart point at a brand-new list. The original survived untouched.

वही type (list, जो बदल सकती है), पर बिल्कुल अलग result। Assignment cart = [...] ने caller की list नहीं बदली — इसने सिर्फ़ local नाम cart को एक बिल्कुल नई list की ओर मोड़ दिया। मूल list अछूती रही।

同样的类型(可变的 list),结果却完全不同。赋值 cart = [...] 并没有修改调用者的 list — 它只是让局部名字 cart 指向一个全新的 list。原来的 list 完好无损。

Object TypeObject का Type对象类型What the Function DoesFunction क्या करता है函数内的操作Does the Caller See It?क्या Caller को दिखता है?调用者能看到吗?
int, str, float, bool, tupleAnything (they cannot be changed)कुछ भी (वे बदल नहीं सकते)任何操作(它们不可修改)Noनहीं否
list, dict, set.append(), .pop(), [i] = v, .update()Yes — mutation leaksहाँ — mutation रिसता है是 — 修改会泄漏
list, dict, setcart = [...] or d = {...} (rebinding)cart = [...] या d = {...} (rebinding)cart = [...] 或 d = {...}(重新绑定)No — only the local name movesनहीं — सिर्फ़ local नाम हटता है否 — 只移动了局部名字
Your own class instanceआपकी अपनी class का instance自定义类的实例Attribute assignment: obj.attr = valueAttribute assignment: obj.attr = value属性赋值:obj.attr = valueYesहाँ是

One fine point: a tuple cannot change, but a list inside a tuple still can. t[0].append(1) works when t = ([],).

एक बारीक बात: tuple बदल नहीं सकता, पर tuple के अंदर रखी list अब भी बदल सकती है। t = ([],) हो तो t[0].append(1) चलता है।

一个细节:tuple 不可修改,但 tuple 里面 的 list 仍然可以修改。当 t = ([],) 时,t[0].append(1) 可以正常执行。

Try It — Reference Visualizerकरके देखें — Reference Visualizer动手试试 — 引用可视化

Names are tags; values are objects. Step through each case and watch the arrows. Passing an argument sticks a second tag on the same object. Then it depends: mutating the object is seen by everyone, rebinding the tag is not.

नाम tags हैं; values objects हैं। हर मामले को कदम-दर-कदम चलाएँ और तीर देखें। Argument देने पर उसी object पर दूसरा tag चिपकता है। फिर यह निर्भर करता है: object को बदलना सबको दिखता है, tag को हटाना नहीं।

名字是 标签;值是 对象。逐步执行每种情况,观察箭头。传参就是在 同一个 对象上再贴一个标签。接下来就看情况了:修改 对象大家都看得到,重新绑定 标签则不会。

🏷️ Reference VisualizerName tags → objects. Mutate vs rebind.नाम tags → objects। Mutate बनाम rebind।名字标签 → 对象。修改与重新绑定。
🛡️
How to Defend Against Accidental Mutationअनचाहे Mutation से कैसे बचें如何防止意外修改

If a function takes a list or dict and you don't want the caller to see changes, copy it at the top: data = data.copy() for a shallow copy, or copy.deepcopy(data) if it contains nested lists or dicts. Or accept a tuple instead of a list — a tuple cannot be changed at all.

अगर function list या dict लेता है और आप नहीं चाहते कि caller को बदलाव दिखें, तो शुरू में ही copy बनाएँ: shallow copy के लिए data = data.copy(), या अंदर nested lists या dicts हों तो copy.deepcopy(data)। या list की जगह tuple लें — tuple बिल्कुल नहीं बदलता।

如果函数接收 list 或 dict,而你不希望调用者看到修改,就在开头复制一份:浅拷贝用 data = data.copy();如果里面有嵌套的 list 或 dict,用 copy.deepcopy(data)。或者改为接收 tuple — tuple 根本无法修改。


SectionSection第 11 节

The Mutable Default Argument Trap 💥बदलने वाले Default Argument का जाल 💥可变默认参数陷阱 💥

This is the most famous Python gotcha. Almost every Python developer meets it at least once — often in production.

यह Python की सबसे मशहूर पकड़ है। लगभग हर Python developer कम से कम एक बार इससे टकराता है — अक्सर production में।

这是 Python 最著名的坑。几乎每个 Python 开发者都至少踩过一次 — 而且往往是在生产环境里。

# The trap:
def append_to(item, target=[]):     # mutable default!
    target.append(item)
    return target

print(append_to(1))
print(append_to(2))
print(append_to(3))
print("stored default:", append_to.__defaults__)
OUTPUT (probably not what you expected)OUTPUT (शायद आपकी उम्मीद से अलग)OUTPUT(可能出乎你的意料)
[1] [1, 2] [1, 2, 3] stored default: ([1, 2, 3],)

Each call was expected to return a fresh [item]. Instead, the same list is reused and grows across calls. Why? Because default values are evaluated once, when the def line runs. That one list object is stored in append_to.__defaults__ — the last line proves it — and every call that does not pass target changes that same object.

हर call से नई [item] लौटने की उम्मीद थी। इसकी जगह वही list बार-बार इस्तेमाल होती है और हर call के साथ बढ़ती जाती है। क्यों? क्योंकि default values एक बार, def line चलने पर बनती हैं। वह एक list object append_to.__defaults__ में रखा जाता है — आख़िरी line इसका सबूत है — और target न देने वाला हर call उसी object को बदलता है।

本来期望每次调用都返回一个新的 [item]。结果同一个 list 被反复使用,越变越长。为什么?因为默认值 只在 def 这一行运行时计算一次。那个 list 对象存放在 append_to.__defaults__ 里 — 最后一行就是证据 — 每次不传 target 的调用,修改的都是这同一个对象。

🛡️
The Fix — Use None as a Markerहल — None को निशान बनाएँ解决办法 — 用 None 作标记

Set the default to None and create a fresh list inside the body. A new list is made on every call, and the trap is gone.

Default को None रखें और body के अंदर नई list बनाएँ। हर call पर नई list बनती है, और जाल ख़त्म।

把默认值设为 None,在函数体里创建新的 list。每次调用都会得到一个新 list,陷阱就消失了。

# The correct pattern:
def append_to(item, target=None):
    if target is None:
        target = []                    # fresh list every call
    target.append(item)
    return target

print(append_to(1))
print(append_to(2))
print(append_to(3))
print("stored default:", append_to.__defaults__)
OUTPUT (as expected)OUTPUT (जैसी उम्मीद थी)OUTPUT(符合预期)
[1] [2] [3] stored default: (None,)

Try It — Mutable Default Trap Labकरके देखें — Mutable Default Trap Lab动手试试 — 可变默认值陷阱实验室

Press Call a few times. In the trap version, look inside the function's hidden drawer (__defaults__) and watch the one default list grow — and every earlier result change with it. Then switch to the fixed version and repeat.

Call कुछ बार दबाएँ। Trap वाले रूप में function की छिपी दराज़ (__defaults__) में देखें और एक default list को बढ़ते देखें — और उसके साथ पहले के हर result को बदलते। फिर fixed रूप पर जाएँ और दोहराएँ।

多按几次 调用。在 陷阱 版本中,看看函数的隐藏抽屉(__defaults__),观察 那一个 默认 list 不断变长 — 之前的每个结果也跟着变。然后切换到 修复 版本再试一次。

💥 Mutable Default Trap LabDefaults are created once — at def time.Defaults एक बार बनते हैं — def के समय।默认值只创建一次 — 在 def 时。
every result so far — shown liveअब तक का हर result — live目前为止的每个结果 — 实时显示

SectionSection第 12 节

A Complete Real-World Example — Building a Flexible Logger 📝एक पूरा असली उदाहरण — लचीला Logger बनाना 📝完整的实战示例 — 构建灵活的日志函数 📝

Let's combine everything: a required parameter, *args, keyword-only parameters with defaults, keyword calls and **kwargs.

अब सब कुछ जोड़ते हैं: एक ज़रूरी parameter, *args, defaults वाले keyword-only parameters, keyword calls और **kwargs।

把所有内容组合起来:一个必需形参、*args、带默认值的仅关键字形参、关键字调用以及 **kwargs。

import datetime

def log(message, *tags, level="INFO", timestamp=True, **metadata):
    """
    Log a message with optional tags, level, timestamp, and metadata.

    - message   : required, the log body
    - *tags     : any number of string tags
    - level     : keyword-only, with a default (INFO, WARN, ERROR)
    - timestamp : keyword-only, whether to add the time
    - **metadata: any extra key=value context (user_id, request_id, ...)
    """
    parts = []

    if timestamp:
        now = datetime.datetime.now().strftime("%H:%M:%S")
        parts.append(f"[{now}]")

    parts.append(f"[{level}]")

    if tags:
        parts.append("#" + " #".join(tags))

    parts.append(message)

    if metadata:
        meta_str = " ".join(f"{k}={v}" for k, v in metadata.items())
        parts.append(f"| {meta_str}")

    print(" ".join(parts))


# Simple call — only the required argument
log("Server started")

# With tags (variable positional)
log("Cache miss", "redis", "user-profile")

# Override a default by KEYWORD — timestamp keeps its default
log("Login failed", level="WARN")

# Everything at once
log(
    "Payment declined",
    "stripe", "billing",             # *tags
    level="ERROR",                     # overrides default
    timestamp=True,                     # same as the default
    user_id=4821,                        # **metadata
    request_id="req-9f3a",
    amount=2500
)
OUTPUT
[06:56:06] [INFO] Server started [06:56:06] [INFO] #redis #user-profile Cache miss [06:56:06] [WARN] Login failed [06:56:06] [ERROR] #stripe #billing Payment declined | user_id=4821 request_id=req-9f3a amount=2500

The time in brackets is the moment the code ran, so yours will be different.

Brackets में समय वह पल है जब code चला, इसलिए आपका अलग होगा।

方括号里的时间是代码运行的时刻,所以你的会不同。

🏆
Notice How Readable Every Call Isदेखें हर Call कितना साफ़ पढ़ा जाता है注意每个调用都多么易读

No call passes True or "WARN" as a mystery positional argument. In fact it cannot: level and timestamp come after *tags, so they are keyword-only. Every optional value has a name at the call site. Mature Python libraries use this pattern — learn it and copy it.

कोई भी call True या "WARN" को रहस्यमय positional argument की तरह नहीं देता। असल में दे ही नहीं सकता: level और timestamp *tags के बाद आते हैं, इसलिए वे keyword-only हैं। Call में हर वैकल्पिक value का नाम है। परिपक्व Python libraries यही तरीका अपनाती हैं — इसे सीखें और अपनाएँ।

没有任何调用把 True 或 "WARN" 当作含义不明的位置参数传入。事实上也 不可能:level 和 timestamp 在 *tags 之后,所以是仅关键字形参。调用处的每个可选值都有名字。成熟的 Python 库都用这种模式 — 学会它,照着用。

Try It — Logger Call Builderकरके देखें — Logger Call Builder动手试试 — 日志调用构建器

Fill in the form and the lab writes the call for you, in the clean keyword style. The call is bound to the real log() signature and printed. Try the chips: passing "WARN" by position, or misspelling level, shows two classic *args/**kwargs traps.

Form भरें और lab आपके लिए साफ़ keyword शैली में call लिख देता है। Call असली log() signature से जोड़ा जाता है और print होता है। Chips आज़माएँ: "WARN" को स्थान से देना, या level की ग़लत spelling, *args/**kwargs के दो मशहूर जाल दिखाते हैं।

填写表单,实验室就会用干净的关键字风格帮你写出调用。调用会绑定到真实的 log() 签名并打印出来。试试这些按钮:按位置传入 "WARN",或把 level 拼错,会展示 *args/**kwargs 的两个经典陷阱。

📝 Logger Call BuilderForm → call → bound parameters → log line.Form → call → जुड़े parameters → log line।表单 → 调用 → 绑定的形参 → 日志行。
Try:आज़माएँ:试试:
show timeसमय दिखाएँ显示时间
=
=
the call (you can also edit it)call (आप इसे बदल भी सकते हैं)调用(也可以直接编辑)

SectionSection第 13 节

Golden Rules 🏆सुनहरे नियम 🏆黄金法则 🏆

🌐 Python Functions — Rules to RememberPython Functions — याद रखने वाले नियमPython 函数 — 必须记住的规则
1
Never use a changeable object as a default argument. Not [], not {}, not set(). Use None as a marker and create the list or dict inside the body. This one rule removes a whole family of bugs.
बदलने वाले object को कभी default argument न बनाएँ। न [], न {}, न set()। None को निशान बनाएँ और list या dict body के अंदर बनाएँ। यह एक नियम bugs के पूरे परिवार को ख़त्म करता है।
永远不要用可变对象作默认参数。不要 [],不要 {},也不要 set()。用 None 作标记,在函数体里创建 list 或 dict。这一条规则就能消灭一整类 bug。
2
Use keyword arguments at call sites whenever a function has more than two parameters or any True/False flags. f(x, y, True, False, None) is unreadable six months later. f(x, y, save=True, verbose=False, on_error=None) is not.
Call करते समय keyword arguments इस्तेमाल करें जब भी function में दो से ज़्यादा parameters या कोई True/False flag हो। f(x, y, True, False, None) छह महीने बाद समझ नहीं आता। f(x, y, save=True, verbose=False, on_error=None) आता है।
在调用处使用关键字参数,只要函数有两个以上的形参或任何 True/False 开关。f(x, y, True, False, None) 六个月后就看不懂了,f(x, y, save=True, verbose=False, on_error=None) 则不会。
3
Return values instead of printing them. A function that only prints cannot be combined, tested or reused. return makes your function a tool. print makes it a dead end.
Values को print करने की जगह return करें। जो function सिर्फ़ print करता है, उसे न जोड़ा जा सकता है, न test किया जा सकता है, न दोबारा इस्तेमाल। return आपके function को औज़ार बनाता है। print उसे बंद गली।
返回值,而不是打印它们。只会 print 的函数无法组合、测试或复用。return 让函数成为工具,print 让它成为死胡同。
4
Remember that Python passes object references. Mutating a list, dict or object attribute inside a function affects the caller. If you don't want that, copy the input at the top or accept unchangeable types.
याद रखें कि Python object references देता है। Function के अंदर list, dict या object attribute को बदलना caller पर असर डालता है। अगर यह नहीं चाहिए, तो शुरू में input की copy बनाएँ या न बदलने वाले types लें।
记住 Python 传递的是对象引用。在函数内修改 list、dict 或对象属性会影响调用者。不想这样,就在开头复制输入,或改为接收不可变类型。
5
Parameter order in def is fixed: positional-only → / → regular → with defaults → *args (or *) → keyword-only → **kwargs. Breaking it is a SyntaxError. Remember: only **kwargs must always be last.
def में parameters का क्रम तय है: सिर्फ़-positional → / → सामान्य → defaults वाले → *args (या *) → keyword-only → **kwargs। इसे तोड़ना SyntaxError है। याद रखें: सिर्फ़ **kwargs को हमेशा आख़िर में होना है।
def 中形参的顺序是固定的:仅位置 → / → 普通 → 带默认值 → *args(或 *)→ 仅关键字 → **kwargs。打乱就是 SyntaxError。记住:只有 **kwargs 必须永远在最后。
6
Make flags keyword-only. Put options like verbose or level after * (or *args), so callers must name them. The logger in Section 12 does exactly this.
Flags को keyword-only बनाएँ। verbose या level जैसे विकल्प * (या *args) के बाद रखें, ताकि callers को उनका नाम लिखना ही पड़े। Section 12 का logger बिल्कुल यही करता है।
让开关参数成为仅关键字形参。把 verbose 或 level 这类选项放在 *(或 *args)之后,调用者就 必须 写出名字。第 12 节的日志函数正是这样做的。
7
Use *args only when the count really is open-ended (like sum-style or print-style functions). If you always expect 3 arguments, declare 3 parameters — clearer signatures make better APIs.
*args तभी इस्तेमाल करें जब संख्या सच में तय न हो (जैसे sum या print जैसे functions)। अगर आप हमेशा 3 arguments की उम्मीद करते हैं, तो 3 parameters लिखें — साफ़ signatures बेहतर APIs बनाते हैं।
只有当数量真的不确定时才用 *args(比如 sum 或 print 这类函数)。如果你总是期望 3 个参数,就声明 3 个形参 — 清晰的签名才是好 API。
8
Use **kwargs for pass-through and extension — for example, to forward options to a lower-level function. Don't use it instead of a well-designed signature: it hides what the function accepts, and typos pass silently.
**kwargs को आगे भेजने और विस्तार के लिए इस्तेमाल करें — जैसे निचले function को विकल्प आगे भेजना। अच्छे signature की जगह इसे न लगाएँ: यह छिपा देता है कि function क्या लेता है, और typos चुपचाप निकल जाते हैं।
用 **kwargs 做透传和扩展 — 例如把选项转发给更底层的函数。不要用它代替精心设计的签名:它会隐藏函数接受什么参数,拼写错误也会悄悄通过。
9
Write a docstring for every non-trivial function. Say what it does, what each parameter means and what it returns. Future-you will thank present-you.
हर ज़रूरी function के लिए docstring लिखें। बताएँ कि यह क्या करता है, हर parameter का क्या मतलब है और यह क्या लौटाता है। भविष्य का आप, आज के आप को धन्यवाद देगा।
为每个不简单的函数写 docstring。说明它做什么、每个形参的含义以及返回什么。将来的你会感谢现在的你。

Quick Checkछोटी जाँच小测验

Seven questions on functions. Pick an answer to see why.

Functions पर सात सवाल। जवाब चुनें और कारण देखें।

关于函数的七道题。选择答案查看解释。

🎯
The Takeawayमुख्य सीख要点总结

A function is a named machine: arguments go in, the body runs in its own frame, and return sends one value back. Match arguments by position or by name, give options sensible unchangeable defaults, use *args/**kwargs for open-ended input, and remember that the caller sees any change you make to a shared list or dict.

Function एक नाम वाली मशीन है: arguments अंदर जाते हैं, body अपने frame में चलती है, और return एक value वापस भेजता है। Arguments को स्थान से या नाम से मिलाएँ, विकल्पों को समझदार न बदलने वाले defaults दें, खुले input के लिए *args/**kwargs इस्तेमाल करें, और याद रखें कि साझा list या dict में किया गया हर बदलाव caller को दिखता है।

函数是一台有名字的机器:实参进去,函数体在自己的帧里运行,return 送回一个值。按位置或按名字匹配实参,给选项合理的 不可变 默认值,用 *args/**kwargs 接收不定数量的输入,并记住:你对共享 list 或 dict 的任何修改,调用者都看得到。

🐍 Try Python Yourself

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