Python新手进阶指南:吃透函数与模块

作者:很菜不狗2026.11.06 22:02浏览量:0

简介:函数与模块是Python学习中承上启下的关键环节。本文系统讲解函数定义、位置/关键字/默认参数、*args与**kwargs、递归三要素,以及模块导入方式、常用内置模块、自定义模块与__name__变量的用法,帮助初学者告别冗长脚本,写出结构清晰、可复用、易维护的代码。

一、为什么函数与模块是新手的关键一关

很多初学者在掌握变量、条件判断、循环之后会遇到瓶颈:脚本越写越长,重复代码越堆越多,改一处错一处。这个阶段的“解药”正是函数与模块,它们分别对应两类核心能力:

  • 函数解决“代码复用”问题:把一段逻辑封装起来,起个名字,随时调用;
  • 模块解决“代码组织”问题:把相关的函数、类收纳进独立文件,按需加载。

理解了这两点,就理解了几乎所有编程语言共通的工程化思路。下面我们逐一拆解。

二、函数:从定义到精通

2.1 用 def 定义函数

  1. def greet(name):
  2. """向指定的人打招呼"""
  3. return f"你好,{name}!"
  4. message = greet("小明")
  5. print(message) # 你好,小明!

几个要点需要牢记:

  • 函数名遵循小写加下划线的命名习惯,做到见名知义;
  • 用文档字符串说明函数的用途、参数与返回值,方便自己和他人阅读;
  • 有 return 则返回结果;没有写 return 的函数默认返回 None,这是新手常忽略的细节。

2.2 三种基础参数传递方式

  1. 位置参数:按顺序一一对应,最直观,但参数多了容易错位;
  2. 关键字参数:调用时写明“参数名=值”,顺序自由、可读性强;
  3. 默认参数:定义时给参数一个默认值,调用时可省略。
  1. def make_coffee(kind, size="中杯", sugar=0):
  2. return f"{size}{kind},加{sugar}份糖"
  3. print(make_coffee("拿铁")) # 全部使用默认值
  4. print(make_coffee("美式", sugar=2)) # 关键字参数
  5. print(make_coffee("摩卡", "大杯", 1)) # 位置参数

新手最容易踩的坑是:默认参数使用可变对象。请看反例:

  1. def add_item(item, items=[]): # 危险写法
  2. items.append(item)
  3. return items
  4. print(add_item(1)) # [1]
  5. print(add_item(2)) # [1, 2],默认列表被两次调用共享了!

原因在于默认值在函数定义时只创建一次。正确做法是把默认值设为 None,在函数体内新建:

  1. def add_item(item, items=None):
  2. if items is None:
  3. items = []
  4. items.append(item)
  5. return items

2.3 args 与 *kwargs:应对不确定数量的参数

当函数需要接受任意数量的参数时:

  • *args 把多余的位置参数收集成一个元组;
  • **kwargs 把多余的关键字参数收集成一个字典。
  1. def report(title, *scores, **details):
  2. print(f"标题:{title}")
  3. print(f"分数:{scores}")
  4. print(f"详情:{details}")
  5. report("月考", 90, 85, 88, subject="数学", cls="三班")

定义参数时的顺序约定为:位置参数 → args → 默认参数/关键字参数 → **kwargs。此外,调用函数时 `和**` 还能反向“解包”序列与字典,把容器中的元素展开传给函数,这在封装和转发参数的场景中非常常用。

2.4 递归函数:自己调用自己

递归适合解决“可以分解为同类子问题”的场景,例如计算阶乘:

  1. def factorial(n):
  2. if n <= 1: # 基线条件:必须存在,否则无限递归
  3. return 1
  4. return n * factorial(n - 1) # 递归条件:规模不断缩小

写递归的三要素:

  1. 基线条件:明确何时停止递归;
  2. 递归条件:每层调用都要让问题规模变小;
  3. 信任递归:相信子调用会返回正确结果,不必在脑中展开每一层。

需要提醒的是,解释器对递归深度有默认限制(约千层级),深层递归应考虑改写为循环,或通过记录中间结果来避免重复计算。

三、模块与包:让代码各归其位

3.1 两种导入方式

  1. import math # 导入整个模块
  2. print(math.sqrt(16)) # 4.0
  3. from datetime import date # 只导入需要的名字
  4. print(date.today())
  • import 模块名:命名空间清晰,不易与其他名字冲突,建议作为默认选择;
  • from 模块 import 名字:调用简短,但应避免 from 模块 import * 这种写法,以免大量名字涌入当前文件,造成覆盖与混乱。

3.2 常用内置模块速览

标准库是随语言自带的“瑞士军刀”,初学者至少应熟悉以下三个:

  • math:数学运算,如 math.sqrt() 开方、math.pi 圆周率、math.ceil() 向上取整;
  • random:随机数,如 random.randint(a, b) 生成区间内随机整数、random.choice() 随机选取、random.shuffle() 打乱序列;
  • datetime:日期时间处理,如 datetime.now() 获取当前时间,配合 timedelta 做时间增减。
  1. import random
  2. from datetime import datetime, timedelta
  3. dice = random.randint(1, 6) # 掷骰子
  4. now = datetime.now()
  5. tomorrow = now + timedelta(days=1) # 明天此刻

养成“先查标准库再造轮子”的习惯,能省下大量时间,也让代码更可靠。

3.3 自定义模块与 name 变量

把函数保存为 my_utils.py,它就是一个模块,同目录下的其他文件通过 import my_utils 即可使用。而 __name__ 是每个模块自带的属性,其取值遵循一条简单规则:

  • 直接运行该文件时,__name__ 等于 "__main__";
  • 被其他文件导入时,__name__ 等于模块名。

利用这一点,可以让一个文件“既能当模块被导入,又能当脚本直接运行”:

  1. # my_utils.py
  2. def add(a, b):
  3. return a + b
  4. if __name__ == "__main__":
  5. # 只有直接运行本文件时才执行,被导入时不会执行
  6. print(add(1, 2))

这是社区约定俗成的代码组织方式,几乎所有成熟代码中都能看到这个结构。当项目继续变大,可以把多个模块放进同一个文件夹并配置 __init__.py,形成“包”,按“包名.模块名”的层级管理代码,目录结构即程序结构。

四、给新手的练习建议

  1. 把之前写过的重复代码重构成函数,刻意练习“拆分逻辑”;
  2. 写一个能接收任意数量成绩并计算平均分与最高分的函数,用上 *args;
  3. 用递归实现斐波那契数列,再思考如何减少其中的重复计算;
  4. 建一个自己的 utils.py,收纳常用工具函数,体会模块化带来的整洁;
  5. 翻阅 math、random、datetime 的官方文档,动手验证每个函数的行为。

五、小结

函数让你复用逻辑,模块让你组织代码。掌握 def 与三类参数的传递规则,理解 args/*kwargs 的收集与解包,写递归时先想清楚终止条件;再用 import 管理依赖,用 name 区分脚本与模块——这些能力共同构成了从“写代码”迈向“做工程”的桥梁。夯实这一阶段后,可以继续学习面向对象与异常处理,让程序结构更上一层楼。