Python 函数传参与 JavaScript 对比
如果你熟悉 JavaScript,用 JS 对比来理解 Python 的函数传参会非常清晰。虽然两者语法不同,但核心思想有很多相似之处,也有关键差异。
📌 总体对比表
| 特性 | JavaScript (ES6+) | Python |
|---|---|---|
| 位置参数 | ✅ 支持 | ✅ 支持 |
| 默认参数 | ✅ 支持 | ✅ 支持 |
| 剩余参数(Rest) | ...args |
*args |
| 展开参数(Spread) | ...array / ...obj |
*list / **dict |
| 命名参数(Named Params) | ❌ 无原生支持(靠对象模拟) | ✅ 原生支持关键字参数 |
| 强制关键字参数 | ❌ 不支持 | ✅ 支持(* 分隔) |
| 仅限位置参数 | ❌ 不支持 | ✅ 支持(/ 分隔,Python 3.8+) |
💡 关键差异:
- JS 没有"关键字参数"概念,靠传对象模拟;
- Python 原生支持位置参数 + 关键字参数混合,更灵活但也更"复杂"。
1️⃣ 基础传参:位置参数 & 默认参数
✅ JavaScript
js
function greet(name, age = 18) {
console.log(`Hello ${name}, you are ${age}`);
}
greet("Alice", 25); // Hello Alice, you are 25
greet("Bob"); // Hello Bob, you are 18
✅ Python(几乎一样)
python
def greet(name, age=18):
print(f"Hello {name}, you are {age}")
greet("Alice", 25) # Hello Alice, you are 25
greet("Bob") # Hello Bob, you are 18
✅ 结论:这部分两者几乎一致,很简单。
2️⃣ "命名参数":JS 靠对象,Python 原生支持
这是最大差异点!
🔸 JavaScript:用对象模拟命名参数
php
function createUser({ name, email, age = 18 }) {
return { name, email, age };
}
// 调用时必须传对象,顺序无关
createUser({ email: "a@example.com", name: "Alice" });
⚠️ 本质还是一个位置参数(对象) ,只是解构了。
🔸 Python:原生关键字参数(Keyword Arguments)
perl
def create_user(name, email, age=18):
return {"name": name, "email": email, "age": age}
# 可以混用位置 + 关键字,顺序自由(关键字在后)
create_user("Alice", email="a@example.com")
create_user(email="a@example.com", name="Alice") # 全用关键字也可以!
✅ 优势:
- 调用时可读性极强(尤其参数多时)
- 顺序不重要(只要位置参数在前)
💡 这就是为什么你在 FastAPI 看到
UserCreate(username="alice", email="...")------ 这是 Python 的原生能力!
3️⃣ 剩余参数(Rest Parameters)
✅ JavaScript:...args
javascript
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // numbers = [1, 2, 3, 4]
✅ Python:*args
python
def sum_all(*numbers):
return sum(numbers)
sum_all(1, 2, 3, 4) # numbers = (1, 2, 3, 4) ← 注意是 tuple!
✅ 几乎一样 !只是 Python 叫 *args,JS 叫 ...args。
4️⃣ 展开操作符(Spread)
✅ JavaScript
ini
const arr = [1, 2, 3];
Math.max(...arr); // 等价于 Math.max(1, 2, 3)
const obj1 = { a: 1 };
const obj2 = { ...obj1, b: 2 }; // { a: 1, b: 2 }
✅ Python
ini
arr = [1, 2, 3]
max(*arr) # 等价于 max(1, 2, 3)
dict1 = {"a": 1}
dict2 = {**dict1, "b": 2} # {"a": 1, "b": 2}
✅ 功能一致!只是符号不同:
- JS 用
... - Python 用
*(列表/元组)和**(字典)
💡 记住:
*→ 解包 序列(list/tuple)→ 位置参数**→ 解包 字典 → 关键字参数
5️⃣ Python 特有:强制关键字参数(Keyword-Only Arguments)
这是 JS 没有的概念,也是你觉得"复杂"的来源之一。
🌰 场景:你想让某些参数必须用关键字传(提高可读性)
python
def connect(host, port, *, timeout=5, ssl=False):
# * 之后的参数只能用关键字传!
print(f"Connect to {host}:{port}, timeout={timeout}, ssl={ssl}")
# 正确调用
connect("localhost", 8000, timeout=10, ssl=True)
# ❌ 错误!不能这样传
connect("localhost", 8000, 10, True) # TypeError!
💡 相当于 JS 中强制你写:
phpconnect("localhost", 8000, { timeout: 10, ssl: true })但 Python 用语法直接实现了!
6️⃣ Python 特有:仅限位置参数(Positional-Only, Python 3.8+)
反过来,有些参数只能按位置传(不能用关键字):
python
def div(x, y, /): # / 之前的参数只能位置传
return x / y
div(10, 2) # ✅ OK
div(x=10, y=2) # ❌ TypeError!
💡 这在底层 API(如 C 扩展)中常见,普通用户较少用。
🧩 综合示例:完整函数签名
Python 函数参数顺序(必须遵守!)
python
def func(
pos_only, /, # 仅限位置
standard, # 标准参数(位置或关键字)
*, # * 之后仅限关键字
kw_only
):
pass
调用方式:
ini
func(1, 2, kw_only=3) # ✅
func(1, standard=2, kw_only=3) # ✅
func(pos_only=1, ...) # ❌ pos_only 不能用关键字
func(1, 2, 3) # ❌ kw_only 必须用关键字
JavaScript 如何模拟?
go
// 只能靠文档约定,无法语法强制
function func(posOnly, standard, options = {}) {
const { kwOnly } = options;
// ...
}
func(1, 2, { kwOnly: 3 }); // 靠对象传"关键字参数"
🆚 总结:JS 开发者如何理解 Python 传参
| 你的 JS 习惯 | 对应的 Python 写法 |
|---|---|
func(a, b, { c, d }) |
func(a, b, c=c, d=d) 或 func(a, b, **options) |
func(...arr) |
func(*arr) |
func({...obj}) |
func(**obj) |
| "我想让参数名显式写出" | → Python 原生支持!直接 func(param=value) |
| "参数太多记不住顺序" | → 全用关键字参数!func(a=1, b=2, c=3) |
🌈 记住口诀 :
*解包列表 → 位置参数
**解包字典 → 关键字参数