你好,我是 hockor,今天咱们继续 Python 系列的函数,我们主要从基础语法对比、闭包、装饰器、异步编程几个方面一些来学习下。
函数定义方式
作为前端,JavaScript提供了多种函数定义方式,包括函数声明、函数表达式和箭头函数,而Python主要使用def关键字进行函数定义。
咱们先来看看 JavaScript 基础函数示例:
javascript
// 函数声明
function greet(name) {
return "Hello, " + name + "!";
}
// 函数表达式
const greetExpression = function(name) {
return "Hello, " + name + "!";
};
// 箭头函数
const greetArrow = (name) => "Hello, " + name + "!";
// 调用函数
console.log(greet("World"));
对比之下呢,Python基础函数定义就比较直接了,就是一个def关键字:
# Python 基础函数
def greet(name):
return f"Hello, {name}!"
# 调用函数
print(greet("World"))
参数处理差异
我们知道 JavaScript 使用剩余参数语法(...args),我们可以接收不定长度的参数数组,而Python使用星号语法(*args, **kwargs)。
JavaScript参数处理:
// 默认参数
function createUser(name, age = 18, city = "北京") {
return {
name: name,
age: age,
city: city
};
}
// 剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(createUser("小明"));
console.log(sum(1, 2, 3, 4)); // 10
Python参数处理:
# 默认参数
def create_user(name, age=18, city="北京"):
return {
"name": name,
"age": age,
"city": city
}
# 可变参数
def sum_numbers(*numbers):
return sum(numbers)
print(create_user("小明"))
print(sum_numbers(1, 2, 3, 4)) # 10
**kwargs(可变关键字参数)
作用:接收任意数量的关键字参数(如 key=value),并将其存储为一个字典。
用途:当函数需要处理不确定数量的命名参数时使用。
def print_user_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_user_info(name="Alice", age=25, city="New York")
# 输出:
# name: Alice
# age: 25
# city: New York
结合使用 *args 和 **kwargs
顺序:必须按照 *args 在前,**kwargs 在后的顺序。
示例:
def example_function(arg1, *args, **kwargs):
print(f"固定参数: {arg1}")
print(f"其他位置参数: {args}")
print(f"关键字参数: {kwargs}")
example_function("Hello", 1, 2, 3, name="Bob", age=30)
# 输出:
# 固定参数: Hello
# 其他位置参数: (1, 2, 3)
# 关键字参数: {'name': 'Bob', 'age': 30}
匿名函数实现
JavaScript的箭头函数在Python中对应的是lambda表达式,他们两者在功能上相似,但语法和使用场景有所不同。
- 箭头函数功能更强大,支持多行代码块,
- lambda函数只能包含单个表达式。
我们来看下他们各自的 demo
JavaScript箭头函数:
// 简单箭头函数
const square = (x) => x * x;
const add = (a, b) => a + b;
// 多行箭头函数
const processData = (data) => {
const filtered = data.filter(x => x > 0);
const doubled = filtered.map(x => x * 2);
return doubled.reduce((sum, x) => sum + x, 0);
};
// 使用
console.log(square(5)); // 25
console.log(add(3, 4)); // 7
Python Lambda函数的基础语法类似于
lambda arguments: expression
-
lambda:关键字,表示定义一个 lambda 函数
-
arguments:函数的参数,可以有多个,用逗号分隔
-
expression:单个表达式,是函数的返回值(不能包含语句)
总结起来就是
-
匿名性:没有函数名
-
简洁性:通常只包含一个表达式
-
一次性使用:适合简单操作,复杂逻辑应使用常规函数
-
可以作为对象传递:可以赋值给变量或作为参数传递
我们来看下具体的 demo
# 简单lambda函数
square = lambda x: x * x
add = lambda a, b: a + b
# 复杂处理需要定义完整函数
def process_data(data):
filtered = list(filter(lambda x: x > 0, data))
doubled = list(map(lambda x: x * 2, filtered))
return sum(doubled)
# 使用
print(square(5)) # 25
print(add(3, 4)) # 7
# 返回两个数中较大的数
max_num = lambda a, b: a if a > b else b
print(max_num(5, 3)) # 5
高阶函数应用
两种语言都原生支持高阶函数,包括map、filter和reduce,但在具体实现和使用方式上有差异。JavaScript的数组方法更加直观,而Python需要显式转换为列表。
JavaScript高阶函数:
const numbers = [1, 2, 3, 4, 5];
// map - 转换数组
const doubled = numbers.map(x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - 过滤数组
const evens = numbers.filter(x => x % 2 === 0);
console.log(evens); // [2, 4]
// reduce - 累积操作
const total = numbers.reduce((sum, x) => sum + x, 0);
console.log(total); // 15
// 链式调用
const result = numbers
.filter(x => x % 2 === 0)
.map(x => x * 2)
.reduce((sum, x) => sum + x, 0);
console.log(result); // 12
Python高阶函数:
numbers = [1, 2, 3, 4, 5]
# map - 转换列表
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # [2, 4, 6, 8, 10]
# filter - 过滤列表
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# reduce - 累积操作
from functools import reduce
total = reduce(lambda sum, x: sum + x, numbers, 0)
print(total) # 15
# 函数式编程风格
result = reduce(
lambda sum, x: sum + x,
map(lambda x: x * 2, filter(lambda x: x % 2 == 0, numbers)),
0
)
print(result) # 12
闭包机制
JavaScript和Python都支持闭包,但在变量访问和修改方面有比较大的差异。JavaScript可以直接修改外部变量,而Python需要使用nonlocal关键字(还记得吗,我们之前有提到过这个关键字)。
JavaScript闭包:
function createCounter() {
let count = 0;
return function() {
count++; // 直接修改外部变量
return count;
};
}
// 使用闭包创建多个独立的计数器
const counter1 = createCounter();
const counter2 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1
// 创建带参数的闭包
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Python闭包:
def create_counter():
count = 0
def counter():
nonlocal count # 声明修改外部变量
count += 1
return count
return counter
# 使用闭包创建多个独立的计数器
counter1 = create_counter()
counter2 = create_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter2()) # 1
# 创建带参数的闭包
def create_multiplier(multiplier):
def multiply(x):
return x * multiplier
return multiply
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
装饰器模式
装饰器是一种高阶函数,它接受一个函数作为参数并返回一个新的函数,用于修改或增强原函数的行为。
目前 JavaScript 需要手动实现装饰器模式,而Python则原生支持装饰器语法糖。
比如一个统计函数执行时间的装饰器:
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Function {func.__name__} took {end-start} seconds")
return result
return wrapper
@timer
def long_running_function():
time.sleep(2)
异步编程
异步是 JS 语言天然支持的特性,也是它发展的根本,从回调函数发展到Promise,再到async/await语法。这是JavaScript的核心优势之一。
// Promise 基础用法
function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url) {
resolve(`数据来自: ${url}`);
} else {
reject(new Error('URL不能为空'));
}
}, 1000);
});
}
// async/await 语法
async function processData() {
try {
const data1 = await fetchData('api/users');
const data2 = await fetchData('api/posts');
console.log('用户数据:', data1);
console.log('帖子数据:', data2);
return { users: data1, posts: data2 };
} catch (error) {
console.error('错误:', error.message);
throw error;
}
}
// 并行异步操作
async function fetchAllData() {
try {
const [users, posts, comments] = await Promise.all([
fetchData('api/users'),
fetchData('api/posts'),
fetchData('api/comments')
]);
return { users, posts, comments };
} catch (error) {
console.error('批量获取失败:', error);
}
}
// 使用
processData().then(result => {
console.log('处理完成:', result);
});
而 Python 是通过 asyncio(标准库, 3.4 版本正式引入) 模块提供异步编程支持,语法与JavaScript相似但有重要差异。
import asyncio
import random
# 异步函数定义
async def fetch_data(url):
# 模拟网络请求延迟
await asyncio.sleep(1)
if url:
return f"数据来自: {url}"
else:
raise ValueError('URL不能为空')
# async/await 语法
async def process_data():
try:
data1 = await fetch_data('api/users')
data2 = await fetch_data('api/posts')
print('用户数据:', data1)
print('帖子数据:', data2)
return {"users": data1, "posts": data2}
except Exception as error:
print('错误:', str(error))
raise error
# 并行异步操作
async def fetch_all_data():
try:
results = await asyncio.gather(
fetch_data('api/users'),
fetch_data('api/posts'),
fetch_data('api/comments')
)
users, posts, comments = results
return {"users": users, "posts": posts, "comments": comments}
except Exception as error:
print('批量获取失败:', error)
# 运行异步函数
async def main():
result = await process_data()
print('处理完成:', result)
all_data = await fetch_all_data()
print('所有数据:', all_data)
# 启动异步程序,需要显示的调用 run
asyncio.run(main())
关键区别
1、事件循环的显式性:
-
Python 需要显式创建和管理事件循环(asyncio.run())
-
JavaScript 引擎内置事件循环,自动调度任务
2、异步上下文要求:
-
Python 中 await 只能在 async def 函数内使用
-
JavaScript 的 await 可以在模块顶层使用(ES2022+)
3、错误处理:
-
Python 使用常规的 try/except
-
JavaScript 使用 try/catch 或 .catch() 链式调用
总结
ok,以上我们了解了 Python 中函数的定义、参数、闭包、装饰器和异步调用等基础概念,其他的比如生成器等后续有机会我们再单独讲一下,最后问一个问题:
在 JavaScript 中,由于变量提升(Hoisting),函数可以在定义之前被调用(函数声明会被提升到作用域顶部),比如下面这个代码
hello(); // 先调用
function hello() {
console.log("Hello from JS!");
}
// 输出: "Hello from JS!"
那么你觉得 Python 中如下的代码会报错吗?
hello()
def hello():
print("Hello from Python!")