写给前端的 Python 教程七-函数

96 阅读7分钟

你好,我是 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!")