【Python实战进阶】9、Python自定义函数完全指南:从零基础到闭包实战

在这里插入图片描述

Python自定义函数完全指南:从零基础到闭包实战,让你的代码更简洁高效!

在日常编程中,你是否遇到过重复复制粘贴代码的烦恼?自定义函数就是解决这一问题的"万能钥匙",本文将带你从基础到进阶全面掌握Python函数的核心用法。

1. 为什么需要自定义函数?——从重复代码说起

先来看一个典型的重复代码场景——没有使用函数的成绩统计程序:

# 无函数版:统计3个学生成绩,代码重复严重
# 第1个学生
try:
    score1 = float(input("请输入第1个学生成绩(0-100):"))
    if score1 <0 or score1>100:
        print("成绩超出范围")
except ValueError:
    print("输入错误,请输数字")

# 第2个学生(复制粘贴第1个的代码,改序号)
try:
    score2 = float(input("请输入第2个学生成绩(0-100):"))
    if score2 <0 or score2>100:
        print("成绩超出范围")
except ValueError:
    print("输入错误,请输数字")

# 第3个学生(再复制一遍...)
try:
    score3 = float(input("请输入第3个学生成绩(0-100):"))
    if score3 <0 or score3>100:
        print("成绩超出范围")
except ValueError:
    print("输入错误,请输数字")

问题分析

  • 重复代码多,修改麻烦
  • 容易出错(比如漏改序号)
  • 扩展困难(统计10个学生要复制10遍)

解决方案:使用自定义函数将重复逻辑打包!

重复代码问题
使用自定义函数
代码复用
逻辑清晰
易于维护
减少代码量
模块化编程
快速定位问题

2. 函数基础:3步写出你的第一个函数

2.1 核心语法结构

def 函数名(参数1, 参数2, ...):
    """函数文档字符串"""
    函数体代码
    return 返回值  # 可选

2.2 实战:将重复代码改造成函数

def get_valid_score(student_num):
    """获取有效的学生成绩"""
    while True:
        try:
            score = float(input(f"请输入第{student_num}个学生成绩(0-100):"))
            if 0 <= score <= 100:
                return score  # 返回有效成绩
            print("错误:成绩需在0-100之间")
        except ValueError:
            print("错误:请输入数字(如88.5)")

# 调用函数
score1 = get_valid_score(1)
score2 = get_valid_score(2)
score3 = get_valid_score(3)

average = (score1 + score2 + score3) / 3
print(f"平均分:{round(average, 2)}分")

函数调用流程

主程序get_valid_score函数调用函数,传入student_num=1执行输入验证逻辑返回有效成绩调用函数,传入student_num=2执行输入验证逻辑返回有效成绩主程序get_valid_score函数

2.3 Python的多态特性

Python函数的强大之处在于参数无需声明类型,支持多种数据类型:

def my_sum(a, b):
    return a + b

print(my_sum(3, 5))           # 8(整数相加)
print(my_sum([1, 2], [3, 4])) # [1, 2, 3, 4](列表拼接)
print(my_sum('hello ', 'world')) # 'hello world'(字符串合并)

3. 函数参数详解:4种核心类型

3.1 位置参数

def add_num(a, b):  # a和b是位置参数
    return a + b

print(add_num(3, 5))  # 3传给a,5传给b,输出8

3.2 关键字参数

def calc_total(price, count, discount):
    return price * count * discount

# 关键字参数调用:顺序可换
total1 = calc_total(price=100, count=5, discount=0.8)
total2 = calc_total(count=5, price=100, discount=0.8)  # 顺序变了,结果一样

3.3 默认值参数

def calc_total(price, count, discount=1.0):  # discount默认值1.0
    return price * count * discount

total1 = calc_total(100, 5)     # 使用默认折扣1.0,输出500
total2 = calc_total(100, 5, 0.8) # 覆盖默认值,输出400

3.4 可变参数(*args)

def calc_average(*args):  # *args接收任意个参数,打包成元组
    if len(args) == 0:
        print("请传入至少1个数字")
        return 0
    return sum(args) / len(args)

print(calc_average(85, 92, 78))        # 输出85.0
print(calc_average(90, 88, 95, 76, 82)) # 输出86.2

参数类型选择指南

选择参数类型
参数数量确定吗
有常用默认值吗
使用*args可变参数
使用默认参数
调用时需要
明确参数含义吗
使用关键字参数
使用位置参数

4. 函数嵌套与作用域

4.1 函数嵌套的优势

优势1:数据隐私保护

def connect_DB():
    def get_DB_configuration():  # 内部函数,外部无法访问
        return host, username, password  # 敏感信息不暴露
    conn = connector.connect(get_DB_configuration())
    return conn

# ❌ 外部无法直接调用
# get_DB_configuration()  # NameError

优势2:减少重复开销

def factorial(input):
    # 输入验证(只执行一次,避免递归重复检查)
    if not isinstance(input, int):
        raise Exception('input must be an integer.')
    if input < 0:
        raise Exception('input must be >= 0')

    def inner_factorial(input):  # 递归核心逻辑
        if input <= 1:
            return 1
        return input * inner_factorial(input - 1)
    
    return inner_factorial(input)

print(factorial(5))  # 120

4.2 变量作用域:局部、全局与nonlocal

# 全局变量
MIN_VALUE = 1

def validation_check():
    global MIN_VALUE  # 声明使用全局变量
    MIN_VALUE += 1    # ✅ 可以修改

def outer():
    x = "local"
    def inner():
        nonlocal x    # 声明使用外部函数变量
        x = 'nonlocal'
        print("inner:", x)  # nonlocal
    inner()
    print("outer:", x)  # nonlocal(已被修改)

validation_check()
print(MIN_VALUE)  # 输出: 2
outer()

变量作用域层次

全局作用域 Global
外层函数作用域 Enclosing
内层函数作用域 Local
内置作用域 Built-in
变量查找顺序
L: Local
E: Enclosing
G: Global
B: Built-in

5. 闭包(Closure):函数式编程精髓

闭包是指外部函数返回内部函数,且内部函数"记住"了外部函数的变量。

def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent  # "记住"了exponent的值
    return exponent_of  # 返回函数对象

# 创建专用函数
square = nth_power(2)  # square现在是一个计算平方的函数
cube = nth_power(3)    # cube现在是一个计算立方的函数

print(square(2))  # 4  (2²)
print(cube(2))    # 8  (2³)

闭包的优势

  • 代码简洁:减少重复参数传递
  • 提升性能:外部初始化工作只执行一次
  • 功能强大:为装饰器(decorator)奠定基础

闭包结构解析

外部函数 nth_power
定义内部函数 exponent_of
返回内部函数对象
记住外部变量 exponent
创建闭包 square = nth_power2
调用 square2
使用记住的exponent值

6. 实战:用函数重构成绩统计程序

将"获取成绩、计算平均分、打印等级、保存文件"封装成函数:

import datetime

# 1. 获取有效成绩(带异常处理)
def get_valid_score(student_num):
    while True:
        try:
            score = float(input(f"请输入第{student_num}个学生成绩(0-100):"))
            if 0 <= score <= 100:
                return score
            print("错误:成绩需在0-100之间")
        except ValueError:
            print("错误:请输入数字(如88.5)")

# 2. 计算平均分(支持任意个成绩)
def calc_average(*scores):
    if not scores:
        return 0
    return sum(scores) / len(scores)

# 3. 打印成绩详情
def print_score_detail(scores, average):
    print("="*30)
    print("学生成绩列表:", [round(s, 2) for s in scores])
    print(f"平均分:{round(average, 2)}分")
    for i, s in enumerate(scores, 1):
        print(f"第{i}个学生成绩:{s},", end="")
        if s >= 90: print("等级:A")
        elif s >= 80: print("等级:B")
        else: print("等级:C")

# 4. 保存成绩到文件
def save_score_to_file(scores, average):
    try:
        with open("score_stat.txt", "w", encoding="utf-8") as f:
            time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            f.write(f"统计时间:{time_str}\n")
            f.write(f"成绩列表:{[round(s,2) for s in scores]}\n")
            f.write(f"平均分:{round(average,2)}分")
        print("成绩已保存到score_stat.txt")
    except IOError as e:
        print(f"保存失败:{e}")

# 主程序
if __name__ == "__main__":
    student_count = 3
    scores = []
    for i in range(1, student_count+1):
        score = get_valid_score(i)
        scores.append(score)
    
    average = calc_average(*scores)  # 拆包列表传给可变参数
    print_score_detail(scores, average)
    save_score_to_file(scores, average)

重构后的优势

  • 逻辑清晰:每个函数职责单一
  • 易于扩展:改student_count即可调整统计人数
  • 便于调试:问题定位到具体函数

7. 新手避坑指南:5个核心原则

  1. 函数名"见名知意":用动词+名词组合(如get_valid_score),别用模糊名字
  2. 一个函数只做一件事:避免"万能函数",否则难维护
  3. 参数不宜过多:超过3个参数优先用关键字参数调用
  4. 处理边界情况:如calc_average处理"无参数"情况,避免报错
  5. 使用if __name__ == "__main__":让函数既能被导入复用,又能单独运行

8. 递归函数与装饰器(进阶)

8.1 递归函数示例

def factorial(n):
    if n == 1:          # 终止条件
        return 1
    else:
        return n * factorial(n - 1)  # 递归调用

print(factorial(5))  # 输出:120

8.2 装饰器示例

def log(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def demo(a, b):
    print(f"Result: {a + b}")

demo(3, 5)
# 输出:
# Calling function: demo
# Result: 8

总结

自定义函数是从"写代码"到"写好代码"的关键一步。通过本文的学习,你应该掌握:

  • ✅ 函数的基本定义和调用方法
  • ✅ 4种参数类型的灵活运用
  • ✅ 变量作用域和嵌套函数的使用
  • ✅ 闭包的原理和实战应用
  • ✅ 函数重构的实际案例

记住:自定义函数不是"高级技巧",而是新手必备的"代码整理工具"。从现在开始,写代码前先想"这段逻辑会不会重复用",把它封装成函数,慢慢就会养成"模块化编程"的思维。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无心水

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值