7.1 自定义函数


文章目录

  • 前言
  • 一、函数基础
    • [1. 函数的定义与调用](#1. 函数的定义与调用)
    • [2. 返回值:return语句](#2. 返回值:return语句)
  • 二、函数参数详解
    • [1. 位置参数(Positional Arguments)](#1. 位置参数(Positional Arguments))
    • [2. 关键字参数(Keyword Arguments)](#2. 关键字参数(Keyword Arguments))
    • [3. 默认参数(Default Arguments)](#3. 默认参数(Default Arguments))
    • [4. 可变参数(*args和**kwargs)](#4. 可变参数(*args和**kwargs))
    • [5. 参数解包(*和**运算符)](#5. 参数解包(*和**运算符))
  • 三、函数作用域
    • [1. 局部变量 vs 全局变量](#1. 局部变量 vs 全局变量)
    • [2. nonlocal关键字](#2. nonlocal关键字)
  • 四、高级函数特性
    • [1. 函数作为参数(高阶函数)](#1. 函数作为参数(高阶函数))
    • [2. 闭包(Closure)](#2. 闭包(Closure))
    • [3. 装饰器(Decorator)基础](#3. 装饰器(Decorator)基础)
    • [4. 生成器函数(Generator Functions)](#4. 生成器函数(Generator Functions))
  • 五、文档字符串和类型提示
    • [1. 文档字符串(Docstrings)](#1. 文档字符串(Docstrings))
    • [2. 类型提示(Type Hints)](#2. 类型提示(Type Hints))

前言

本文主要介绍了函数基础、函数参数详解、函数作用域和高级函数特性等知识点。


一、函数基础

1. 函数的定义与调用

python 复制代码
python
# 定义一个简单的函数
def greet():
    """这是一个简单的问候函数"""
    print("Hello, World!")

# 调用函数
greet()  # 输出: Hello, World!
greet()  # 可以多次调用
greet()

# 带参数的函数
def greet_person(name):
    """向指定的人打招呼"""
    print(f"Hello, {name}!")

# 调用带参数的函数
greet_person("Alice")      # 输出: Hello, Alice!
greet_person("Bob")        # 输出: Hello, Bob!

# 带多个参数的函数
def introduce(name, age, city):
    """介绍一个人"""
    print(f"我叫{name},今年{age}岁,来自{city}。")

introduce("张三", 25, "北京")  # 输出: 我叫张三,今年25岁,来自北京。

2. 返回值:return语句

python 复制代码
python
# 没有return语句的函数返回None
def add_without_return(a, b):
    """这个函数没有return语句"""
    result = a + b
    # 注意:没有return!

print(add_without_return(3, 5))  # 输出: None

# 有return语句的函数
def add(a, b):
    """返回两个数的和"""
    result = a + b
    return result

sum_result = add(3, 5)
print(f"3 + 5 = {sum_result}")  # 输出: 3 + 5 = 8

# 可以立即使用返回值
print(f"10 + 20 = {add(10, 20)}")  # 输出: 10 + 20 = 30

# 多个return语句
def get_grade(score):
    """根据分数返回等级"""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

print(f"85分 -> {get_grade(85)}")  # 输出: B
print(f"95分 -> {get_grade(95)}")  # 输出: A
print(f"55分 -> {get_grade(55)}")  # 输出: F

# 返回多个值(实际上是返回一个元组)
def calculate_stats(numbers):
    """计算统计信息"""
    count = len(numbers)
    total = sum(numbers)
    average = total / count if count > 0 else 0
    maximum = max(numbers) if numbers else 0
    minimum = min(numbers) if numbers else 0
    
    return count, total, average, maximum, minimum

# 接收多个返回值
scores = [85, 92, 78, 90, 88]
count, total, avg, max_val, min_val = calculate_stats(scores)

print(f"统计结果:")
print(f"  数量: {count}")
print(f"  总分: {total}")
print(f"  平均分: {avg:.2f}")
print(f"  最高分: {max_val}")
print(f"  最低分: {min_val}")

# 也可以作为元组接收
stats = calculate_stats(scores)
print(f"\n元组形式: {stats}")
print(f"平均分: {stats[2]:.2f}")

二、函数参数详解

1. 位置参数(Positional Arguments)

python 复制代码
python
def describe_pet(animal_type, pet_name):
    """描述宠物"""
    print(f"I have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name}.")

# 位置参数必须按顺序传递
describe_pet("dog", "Buddy")
# 输出:
# I have a dog.
# My dog's name is Buddy.

describe_pet("Buddy", "dog")  # 参数顺序错误!
# 输出:
# I have a Buddy.  # 错误!
# My Buddy's name is dog.

2. 关键字参数(Keyword Arguments)

python 复制代码
python
def describe_pet(animal_type, pet_name):
    """描述宠物"""
    print(f"I have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name}.")

# 使用关键字参数,顺序不重要
describe_pet(pet_name="Buddy", animal_type="dog")
describe_pet(animal_type="cat", pet_name="Whiskers")

# 混合使用位置参数和关键字参数
# 规则:位置参数必须在关键字参数之前
describe_pet("hamster", pet_name="Nibbles")  # 正确
# describe_pet(pet_name="Nibbles", "hamster")  # 错误!位置参数必须在关键字参数之前

3. 默认参数(Default Arguments)

python 复制代码
python
def describe_pet(pet_name, animal_type="dog"):
    """描述宠物,animal_type有默认值"""
    print(f"I have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name}.")

# 使用默认参数
describe_pet("Buddy")  # animal_type使用默认值"dog"
# 输出:
# I have a dog.
# My dog's name is Buddy.

describe_pet("Whiskers", "cat")  # 提供animal_type参数
describe_pet("Nibbles", animal_type="hamster")  # 使用关键字参数

# 重要:默认参数的值在函数定义时计算
def add_item(item, items=[]):  # 危险!默认值是可变对象
    """添加项目到列表(有问题的实现)"""
    items.append(item)
    return items

# 测试
print(add_item("apple"))   # ['apple']
print(add_item("banana"))  # ['apple', 'banana'] 注意:保留了上次的结果!
print(add_item("orange"))  # ['apple', 'banana', 'orange']

# 正确的做法:使用None作为默认值
def add_item_safe(item, items=None):
    """添加项目到列表(安全的实现)"""
    if items is None:
        items = []  # 每次调用都创建新列表
    items.append(item)
    return items

print(add_item_safe("apple"))   # ['apple']
print(add_item_safe("banana"))  # ['banana'] 正确:每次都是新列表
print(add_item_safe("orange"))  # ['orange']

4. 可变参数(*args和**kwargs)

python 复制代码
python
# *args:接收任意数量的位置参数
def sum_all(*args):
    """计算所有参数的和"""
    print(f"参数: {args} (类型: {type(args)})")
    return sum(args)

print(f"sum_all(1, 2, 3) = {sum_all(1, 2, 3)}")
# 输出:
# 参数: (1, 2, 3) (类型: <class 'tuple'>)
# sum_all(1, 2, 3) = 6

print(f"sum_all(10, 20, 30, 40, 50) = {sum_all(10, 20, 30, 40, 50)}")
# 输出: 150

# **kwargs:接收任意数量的关键字参数
def print_info(**kwargs):
    """打印所有关键字参数"""
    print(f"关键字参数: {kwargs} (类型: {type(kwargs)})")
    for key, value in kwargs.items():
        print(f"  {key}: {value}")

print_info(name="Alice", age=25, city="New York")
# 输出:
# 关键字参数: {'name': 'Alice', 'age': 25, 'city': 'New York'} (类型: <class 'dict'>)
#   name: Alice
#   age: 25
#   city: New York

# 混合使用所有参数类型
def complex_function(positional, default="default", *args, **kwargs):
    """演示所有参数类型的函数"""
    print(f"位置参数: {positional}")
    print(f"默认参数: {default}")
    print(f"可变位置参数 (*args): {args}")
    print(f"可变关键字参数 (**kwargs): {kwargs}")
    print("-" * 40)

# 调用示例
complex_function("第一位置参数")
complex_function("第一位置参数", "自定义默认值")
complex_function("第一位置参数", "自定义默认值", "额外1", "额外2")
complex_function("第一位置参数", "自定义默认值", "额外1", "额外2", key1="值1", key2="值2")

# 实际应用:配置函数
def configure_database(host, port=5432, *args, **kwargs):
    """配置数据库连接"""
    print(f"数据库配置:")
    print(f"  主机: {host}")
    print(f"  端口: {port}")
    
    if args:
        print(f"  额外位置参数: {args}")
    
    if kwargs:
        print("  额外选项:")
        for key, value in kwargs.items():
            print(f"    {key}: {value}")
    
    return {"host": host, "port": port, **kwargs}

# 使用
config = configure_database("localhost", 3306, "extra_arg", username="admin", password="secret", database="test")

5. 参数解包(*和**运算符)

python 复制代码
python
# 使用*解包列表/元组
def print_coordinates(x, y, z):
    """打印三维坐标"""
    print(f"坐标: ({x}, {y}, {z})")

point = [10, 20, 30]
print_coordinates(*point)  # 等价于 print_coordinates(10, 20, 30)

# 使用**解包字典
def print_person_info(name, age, city):
    """打印个人信息"""
    print(f"姓名: {name}")
    print(f"年龄: {age}")
    print(f"城市: {city}")

person = {"name": "张三", "age": 25, "city": "北京"}
print_person_info(**person)  # 等价于 print_person_info(name="张三", age=25, city="北京")

# 结合使用
def complex_function(a, b, c, d, e):
    """复杂函数示例"""
    print(f"a={a}, b={b}, c={c}, d={d}, e={e}")

args_list = [1, 2]
kwargs_dict = {"d": 4, "e": 5}
complex_function(*args_list, 3, **kwargs_dict)  # a=1, b=2, c=3, d=4, e=5

三、函数作用域

1. 局部变量 vs 全局变量

python 复制代码
python
# 全局变量
global_var = "我是全局变量"

def function1():
    """函数1:使用全局变量"""
    print(f"在function1中: {global_var}")

def function2():
    """函数2:创建同名局部变量"""
    global_var = "我是局部变量"  # 创建新的局部变量,不影响全局变量
    print(f"在function2中: {global_var}")

def function3():
    """函数3:修改全局变量"""
    global global_var  # 声明使用全局变量
    global_var = "我修改了全局变量"
    print(f"在function3中: {global_var}")

# 测试
print(f"初始全局变量: {global_var}")  # 我是全局变量
function1()                           # 在function1中: 我是全局变量
function2()                           # 在function2中: 我是局部变量
print(f"调用function2后: {global_var}")  # 我还是全局变量(未改变)
function3()                           # 在function3中: 我修改了全局变量
print(f"调用function3后: {global_var}")  # 我修改了全局变量

# 嵌套作用域
def outer_function():
    """外层函数"""
    outer_var = "外层变量"
    
    def inner_function():
        """内层函数"""
        inner_var = "内层变量"
        print(f"内层函数可以访问: {outer_var}")  # 可以访问外层变量
        print(f"内层函数有自己的: {inner_var}")
    
    inner_function()
    # print(f"外层函数不能访问: {inner_var}")  # 错误!不能访问内层变量

outer_function()

2. nonlocal关键字

python 复制代码
python
def outer():
    """外层函数"""
    count = 0
    
    def inner():
        """内层函数 - 修改外层变量"""
        nonlocal count  # 声明使用外层变量
        count += 1
        return count
    
    return inner

# 创建闭包
counter = outer()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

# 对比:不使用nonlocal
def outer_no_nonlocal():
    """外层函数"""
    count = 0
    
    def inner():
        """内层函数 - 不能修改外层变量"""
        # 这里会创建新的局部变量count
        count = 10  # 这是新的局部变量,不是修改外层变量
        return count
    
    print(f"调用inner前: count = {count}")
    result = inner()
    print(f"调用inner后: count = {count}, inner返回: {result}")
    return count

result = outer_no_nonlocal()
# 输出:
# 调用inner前: count = 0
# 调用inner后: count = 0, inner返回: 10

四、高级函数特性

1. 函数作为参数(高阶函数)

python 复制代码
python
def apply_operation(numbers, operation):
    """对列表中的每个元素应用操作"""
    return [operation(x) for x in numbers]

def square(x):
    """平方"""
    return x ** 2

def double(x):
    """加倍"""
    return x * 2

def add_five(x):
    """加5"""
    return x + 5

# 测试
numbers = [1, 2, 3, 4, 5]

print(f"原列表: {numbers}")
print(f"平方: {apply_operation(numbers, square)}")
print(f"加倍: {apply_operation(numbers, double)}")
print(f"加5: {apply_operation(numbers, add_five)}")

# 使用lambda函数(匿名函数)
print(f"立方: {apply_operation(numbers, lambda x: x ** 3)}")
print(f"平方根: {apply_operation(numbers, lambda x: x ** 0.5)}")

# 内置高阶函数示例
from functools import reduce

numbers = [1, 2, 3, 4, 5]

# map: 应用函数到每个元素
squares = list(map(lambda x: x**2, numbers))
print(f"map平方: {squares}")

# filter: 过滤元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"filter偶数: {evens}")

# reduce: 累积计算
product = reduce(lambda x, y: x * y, numbers)
print(f"reduce乘积: {product}")

2. 闭包(Closure)

python 复制代码
python
def make_multiplier(factor):
    """创建乘法器闭包"""
    def multiplier(x):
        return x * factor
    
    return multiplier

# 创建不同的乘法器
double = make_multiplier(2)
triple = make_multiplier(3)
quadruple = make_multiplier(4)

print(f"double(5) = {double(5)}")        # 10
print(f"triple(5) = {triple(5)}")        # 15
print(f"quadruple(5) = {quadruple(5)}")  # 20

# 更复杂的闭包:计数器工厂
def create_counter(start=0, step=1):
    """创建计数器闭包"""
    count = start
    
    def counter():
        nonlocal count
        current = count
        count += step
        return current
    
    return counter

# 创建不同的计数器
simple_counter = create_counter()
even_counter = create_counter(start=0, step=2)
odd_counter = create_counter(start=1, step=2)

print("\n简单计数器:")
for _ in range(5):
    print(simple_counter(), end=" ")  # 0 1 2 3 4

print("\n偶数计数器:")
for _ in range(5):
    print(even_counter(), end=" ")    # 0 2 4 6 8

print("\n奇数计数器:")
for _ in range(5):
    print(odd_counter(), end=" ")     # 1 3 5 7 9

3. 装饰器(Decorator)基础

python 复制代码
python
# 简单的装饰器
def simple_decorator(func):
    """简单的装饰器:在函数调用前后打印信息"""
    def wrapper():
        print("函数调用前...")
        result = func()
        print("函数调用后...")
        return result
    
    return wrapper

@simple_decorator
def say_hello():
    """被装饰的函数"""
    print("Hello!")

# 使用装饰器
say_hello()
# 输出:
# 函数调用前...
# Hello!
# 函数调用后...

# 带参数的装饰器
def repeat(n):
    """重复执行n次的装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for i in range(n):
                print(f"第{i+1}次执行:")
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    """被装饰的函数"""
    print(f"Hello, {name}!")
    return f"Greeted {name}"

print("\n重复执行装饰器:")
results = greet("Alice")
print(f"所有结果: {results}")

# 实用的装饰器:计时器
import time

def timer(func):
    """计时装饰器"""
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 执行时间: {end_time - start_time:.6f}秒")
        return result
    return wrapper

@timer
def slow_function():
    """模拟耗时操作"""
    time.sleep(0.5)
    return "完成"

@timer
def fast_function():
    """快速函数"""
    return "快速完成"

print("\n计时装饰器:")
slow_function()
fast_function()

4. 生成器函数(Generator Functions)

python 复制代码
python
# 普通函数 vs 生成器函数
def normal_range(n):
    """普通函数:返回列表"""
    result = []
    for i in range(n):
        result.append(i)
    return result

def generator_range(n):
    """生成器函数:使用yield"""
    for i in range(n):
        yield i

# 比较内存使用
print("普通函数(占用内存):")
normal_result = normal_range(1000000)
print(f"类型: {type(normal_result)}")
print(f"内存大小: {normal_result.__sizeof__():,} bytes")

print("\n生成器函数(节省内存):")
gen_result = generator_range(1000000)
print(f"类型: {type(gen_result)}")
print(f"内存大小: {gen_result.__sizeof__():,} bytes")

# 使用生成器
print("\n使用生成器:")
counter = generator_range(5)
print(next(counter))  # 0
print(next(counter))  # 1
print(next(counter))  # 2

# 使用for循环
print("使用for循环:")
for value in generator_range(3):
    print(value)

# 实用的生成器:读取大文件
def read_large_file(file_path):
    """逐行读取大文件"""
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            yield line.strip()

# 模拟使用
def process_file():
    """处理文件内容"""
    # 假设我们有一个大文件
    # for line in read_large_file("large_file.txt"):
    #     process_line(line)
    print("这里会逐行处理大文件,而不一次加载到内存")

process_file()

五、文档字符串和类型提示

1. 文档字符串(Docstrings)

python 复制代码
python
def calculate_bmi(weight, height):
    """
    计算身体质量指数(BMI)。
    
    参数:
    weight (float): 体重,单位千克
    height (float): 身高,单位米
    
    返回:
    float: BMI值
    str: BMI分类
    
    示例:
    >>> calculate_bmi(70, 1.75)
    (22.86, '正常')
    
    公式:
    BMI = weight / (height ** 2)
    """
    bmi = weight / (height ** 2)
    
    if bmi < 18.5:
        category = "偏瘦"
    elif bmi < 24:
        category = "正常"
    elif bmi < 28:
        category = "超重"
    else:
        category = "肥胖"
    
    return round(bmi, 2), category

# 查看文档字符串
print("文档字符串:")
print(calculate_bmi.__doc__)

# 使用help函数
# help(calculate_bmi)

# 使用函数
bmi, category = calculate_bmi(70, 1.75)
print(f"\nBMI: {bmi}, 分类: {category}")

2. 类型提示(Type Hints)

python 复制代码
python
from typing import List, Tuple, Dict, Optional, Union

def process_data(
    numbers: List[int],
    multiplier: float = 1.0,
    include_negatives: bool = False
) -> Tuple[List[float], Dict[str, Union[int, float]]]:
    """
    处理数字列表,返回处理结果和统计信息。
    
    类型提示示例:
    - List[int]: 整数列表
    - float: 浮点数
    - bool: 布尔值
    - Tuple[...]: 元组
    - Dict[str, Union[int, float]]: 字典,值为整数或浮点数
    """
    if not include_negatives:
        numbers = [n for n in numbers if n >= 0]
    
    processed = [n * multiplier for n in numbers]
    
    stats = {
        "count": len(processed),
        "sum": sum(processed),
        "average": sum(processed) / len(processed) if processed else 0,
        "max": max(processed) if processed else 0,
        "min": min(processed) if processed else 0
    }
    
    return processed, stats

# 使用
data = [1, 2, 3, 4, 5, -1, -2]
processed, stats = process_data(data, multiplier=2.0, include_negatives=False)

print(f"处理后的数据: {processed}")
print(f"统计信息: {stats}")

# Optional类型:可能为None
def find_user(user_id: int) -> Optional[Dict]:
    """根据ID查找用户,可能返回None"""
    users = {
        1: {"name": "Alice", "age": 25},
        2: {"name": "Bob", "age": 30}
    }
    return users.get(user_id)

# 使用
user = find_user(1)
if user:
    print(f"找到用户: {user}")
else:
    print("用户不存在")

相关推荐
BlackWolfSky2 小时前
React中文网课程笔记1—快速入门
前端·笔记·react.js
import_random2 小时前
[虚拟环境]venv工具(实战)
python
A_one20102 小时前
利用npm内置命令构建脚本工具
前端·npm·node.js
哔哩哔哩技术2 小时前
2025年哔哩哔哩技术精选技术干货
前端·后端·架构
霍理迪2 小时前
CSS布局方式——定位
前端·css
星光不问赶路人2 小时前
TypeScript 架构实践:从后端接口到 UI 渲染数据流的完整方案
前端·vue.js·typescript
ttyyttemo2 小时前
Dagger技术的使用学习
前端
IT_陈寒2 小时前
Redis性能翻倍的5个关键策略:从慢查询到百万QPS的实战优化
前端·人工智能·后端
蓝眸少年CY2 小时前
测试Java性能
java·开发语言·python