Skip to content

Python vs. JavaScript: 函数对比指南

函数是任何编程语言中组织和复用代码的基本构建块。本文档将深入探讨 Python 和 JavaScript 在函数定义、参数处理、返回值以及上下文等方面的核心异同。

参考资料:


核心差异速览

特性 (Feature)PythonJavaScript
定义关键字deffunction / => (箭头函数)
匿名函数lambda (单表达式,功能受限)function() {} / () => {} (功能完整)
参数数量严格 (数量必须匹配,否则报错)灵活 (可多可少,少的为 undefined)
命名/关键字参数原生支持 (func(name="a"))不支持 (通过传递对象模拟)
任意参数收集*args (位置参数) 和 **kwargs (关键字参数)...rest (剩余参数)
方法中的实例引用显式传递 self 作为第一个参数隐式提供 this 上下文,其指向动态变化
多返回值通过返回元组并自动解包实现通过返回数组对象并解构实现

1. 函数定义与调用

基本语法

Python: 使用 def 关键字,后跟函数名、参数列表、冒号 (:) 和缩进的代码块。

python
def greet(name):
    return f"Hello, {name}!"

message = greet("Alice")
print(message) # -> "Hello, Alice!"

JavaScript: 传统上使用 function 关键字,后跟函数名、参数列表和花括号 {} 包围的代码块。

javascript
function greet(name) {
  return `Hello, ${name}!`;
}

let message = greet("Alice");
console.log(message); // -> "Hello, Alice!"

函数表达式与匿名函数

Python: lambda Python 的匿名函数使用 lambda 关键字,但功能非常受限:它只能包含一个表达式,不能包含多条语句。

python
# 一个 lambda 函数,接收两个参数并返回它们的和
add = lambda x, y: x + y
print(add(2, 3)) # -> 5

# 通常用在高阶函数中
numbers = [1, 2, 3, 4]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # -> [2, 4, 6, 8]

JavaScript: 箭头函数 => (ES6+) JavaScript 的匿名函数(特别是箭头函数)功能非常完整,可以包含任意复杂的代码块。箭头函数不仅语法简洁,还会改变 this 的绑定行为。

javascript
// 箭头函数表达式
const add = (x, y) => x + y;
console.log(add(2, 3)); // -> 5

// 用于高阶函数
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(x => x * 2);
console.log(doubled); // -> [2, 4, 6, 8]

// 包含多行语句的箭头函数
const complex_func = (a, b) => {
    const sum = a + b;
    return sum / 2;
};

2. 参数处理 (Parameters & Arguments)

参数数量的严格性

Python: 严格 调用函数时传递的参数数量必须与定义时严格匹配,否则会抛出 TypeError

python
def multiply(a, b):
    return a * b

# multiply(5) # -> TypeError: multiply() missing 1 required positional argument: 'b'
# multiply(5, 2, 1) # -> TypeError: multiply() takes 2 positional arguments but 3 were given

JavaScript: 灵活 可以传递比定义时更多或更少的参数。

  • 少传: 未传递的参数值默认为 undefined
  • 多传: 多余的参数会被忽略,但可以通过 arguments 对象或剩余参数 (...) 访问。
javascript
function multiply(a, b) {
  console.log(`a=${a}, b=${b}`);
  return a * b;
}

multiply(5);      // -> a=5, b=undefined  -> NaN
multiply(5, 2, 1);  // -> a=5, b=2          -> 10 (多余的 1 被忽略)

默认参数

两种语言都支持,语法几乎相同。

Python:

python
def create_user(name, is_admin=False):
    print(f"User: {name}, Admin: {is_admin}")

create_user("Bob") # -> User: Bob, Admin: False

JavaScript:

javascript
function createUser(name, isAdmin = false) {
  console.log(`User: ${name}, Admin: ${isAdmin}`);
}

createUser("Bob"); // -> User: Bob, Admin: false

命名/关键字参数 (Python 独有)

Python: 这是一个强大特性,允许调用者通过 参数名=值 的形式传递参数,从而不必关心参数的顺序。

python
def create_database(host, port, username, password):
    # ...
    print(f"Connecting to {host}:{port} with user {username}")

# 使用关键字参数,顺序可以打乱
create_database(
    username="admin",
    password="123",
    port=5432,
    host="localhost"
)

JavaScript: 没有原生关键字参数。但可以通过传递一个对象作为参数来优雅地模拟此行为,这在 JS 社区是非常普遍的模式。

javascript
function createDatabase({ host, port, username, password }) {
  // ...
  console.log(`Connecting to ${host}:${port} with user ${username}`);
}

// 传递一个对象,键的顺序无关紧要
createDatabase({
  username: "admin",
  password: "123",
  port: 5432,
  host: "localhost"
});

任意数量的参数

Python: *args**kwargs

  • *args: 将所有未匹配的位置参数收集到一个元组 (tuple) 中。
  • **kwargs: 将所有未匹配的关键字参数收集到一个字典 (dict) 中。
python
def process_data(*args, **kwargs):
    print("Positional args:", args)
    print("Keyword args:", kwargs)

process_data(1, "hello", True, user="root", id=101)
# Positional args: (1, 'hello', True)
# Keyword args: {'user': 'root', 'id': 101}

JavaScript: 剩余参数 (...rest) 将所有剩余的参数收集到一个数组 (Array) 中。

javascript
function processData(firstArg, ...rest) {
  console.log("First arg:", firstArg);
  console.log("Rest of args:", rest);
}

processData(1, "hello", true, { user: "root", id: 101 });
// First arg: 1
// Rest of args: [ 'hello', true, { user: 'root', id: 101 } ]

3. 返回值 (Return Values)

多返回值

Python: Python 函数可以轻松返回多个值,其本质是返回一个元组 (tuple),并在接收时自动解包。

python
def get_user_info():
    return "Alice", 30, "alice@example.com" # 实际返回的是 ("Alice", 30, ...)

name, age, email = get_user_info()
print(name) # -> "Alice"

JavaScript: 需要通过返回一个数组对象并手动解构来模拟。

javascript
// 通过数组返回
function getUserInfoAsArray() {
  return ["Alice", 30, "alice@example.com"];
}
const [name, age, email] = getUserInfoAsArray();

// 通过对象返回 (更具可读性)
function getUserInfoAsObject() {
  return { name: "Alice", age: 30, email: "alice@example.com" };
}
const { name, age, email } = getUserInfoAsObject();

4. 作用域与上下文: self vs. this

在面向对象的类方法中,引用实例本身的方式有根本不同。

Python: self 在 Python 中,实例方法必须显式地将实例自身作为第一个参数,并按惯例命名为 self

python
class Circle:
    def __init__(self, radius):
        self.radius = radius # self 显式存在

    def get_area(self): # self 必须作为第一个参数
        return 3.14 * self.radius ** 2

JavaScript: this 在 JavaScript 中,this 是一个隐式存在的上下文关键字,它的值是动态的,取决于函数是如何被调用的。这是 JS 中最复杂也最关键的概念之一。

javascript
class Circle {
  constructor(radius) {
    this.radius = radius; // this 隐式存在
  }

  getArea() { // this 不需要作为参数
    return 3.14 * this.radius ** 2;
  }
}

5. 深入 Lambda 表达式:Python 的设计哲学

虽然我们在前面已经介绍了 lambda,但理解其背后的设计哲学对于真正掌握 Python 至关重要。lambda 经常被初学者误用,因为它看起来像是 def 的一个更短的替代品,但事实并非如此。

参考资料: What is the purpose of Lambda expressions? - Python Discourse

lambda 的核心目的:匿名

lambda 的核心目的是创建匿名函数。可以把它理解为一个没有名字的、一次性的"便签函数"。当你需要一个简单的函数,但又不想为了只用一次的功能而去费心定义一个完整的、有名字的函数时,lambda 就派上了用场。

常见的误用

一个非常典型的误用是将 lambda 表达式赋值给一个变量:

python
# 不推荐的写法
add = lambda x, y: x + y

这种写法完全违背了 lambda "匿名"的初衷。它有几个显著的缺点:

  1. 可读性差: def 语句能清晰地表明"我正在定义一个函数",而 lambda 赋值则相对隐晦。
  2. 调试困难: 如果使用 def 定义的 add 函数出错,错误追踪信息会明确显示 in function add。而 lambda 函数出错时,你只会看到一个通用的 <lambda>,这在复杂的代码中会大大增加调试难度。
  3. 功能受限: lambda 无法包含文档字符串(docstrings),也无法进行类型注解。

正确的做法是,如果你需要一个可以复用的函数,就应该使用 def

python
# 推荐的写法
def add(x, y):
    """返回两个数字的和。"""
    return x + y

这遵循了 Python 之禅(Zen of Python)中的"Explicit is better than implicit"(明确优于隐晦)和"Readability counts"(可读性至上)的原则。

正确的使用场景:作为高阶函数的参数

lambda 真正的用武之地是作为高阶函数(一个接收其他函数作为参数的函数)的参数。

示例:使用 sorted()key 参数 假设你有一个包含字典的列表,想根据每个字典的 "age" 键来排序:

python
people = [
    {'name': 'Alice', 'age': 30},
    {'name': 'Bob', 'age': 25},
    {'name': 'Charlie', 'age': 35}
]

# 使用 lambda 提供一个临时的、简单的 key 函数
sorted_people = sorted(people, key=lambda person: person['age'])

# sorted_people -> [{'name': 'Bob', 'age': 25}, ...]

在这个例子中,我们需要的排序逻辑非常简单("从字典里取出 'age' 的值"),专门为此定义一个 def get_age(person): return person['age'] 会显得很冗余。lambda 在这里完美地扮演了一个临时的、匿名的功能性角色。

设计限制:单一表达式

lambda 函数体内只能包含一个表达式,不能包含 if 语句、for 循环或 print() 等多条语句。

这个限制是有意为之的。它强制 lambda 保持简单。如果你的逻辑需要超过一个表达式才能完成,Python 的设计者认为,你就不应该再使用 lambda 了,而是应该定义一个结构更清晰、功能更完整的 def 函数。

总结:lambda 不是 def 的替代品

  • def: 用于定义命名的、可复用的、功能相对完整的函数。这是创建函数的标准和首选方式。
  • lambda: 用于创建匿名的、一次性的、功能极其简单的单表达式函数,主要作为高阶函数的参数使用。

lambda 是 Python 工具箱中一把锋利的小刀,适用于特定的精细操作,但不应被当作可以替代 def 这把"主厨刀"的工具。