Skip to content

第 04 章 · 函数:把代码打包成工具 ​

本章目标 ​

会定义函数、传参数、拿返回值,理解作用域,写出可复用、可读的代码。

4.1 为什么要函数 ​

不用函数时,同样三行代码要抄三遍;抄错一处就出 bug。函数就是给一段逻辑起个名字,以后叫名字就行。

python
def greet(name):
    """打招呼(这是文档字符串 docstring)"""
    print(f"你好,{name}!")

greet("小明")
greet("小红")
  • def 开头,函数名后跟括号和冒号
  • 函数体缩进
  • 调用时才执行,定义时不执行

4.2 参数与返回值 ​

python
def add(a, b):
    return a + b

result = add(1, 2)
print(result)          # 3

return 做两件事:返回值 + 立刻结束函数。

python
def check(n):
    if n < 0:
        return "负数"     # 到这里就结束了
    return "非负数"

print(check(-5))

没有 return(或只写 return)时,函数返回 None:

python
def show():
    print("只做事,不返回")

print(show())      # 先打印"只做事,不返回",再打印 None

4.3 参数的各种写法 ​

默认值参数 ​

python
def greet(name, greeting="你好"):
    return f"{greeting},{name}"

print(greet("小明"))              # 你好,小明
print(greet("Tom", "Hello"))      # Hello,Tom

⚠️ 默认值别用可变对象(列表/字典),会被"记住":

python
def bad(item, box=[]):    # ❌ 不要这样写
    box.append(item)
    return box

print(bad(1))    # [1]
print(bad(2))    # [1, 2]  ← 以为会重新开始,结果攒起来了

def good(item, box=None):  # ✅ 正确写法
    if box is None:
        box = []
    box.append(item)
    return box

关键字参数 ​

调用时写参数名,顺序就随便了,代码更清楚:

python
def create_user(name, age, city):
    return f"{name}/{age}/{city}"

print(create_user(city="鄂尔多斯", name="Rick", age=30))

可变参数 *args / **kwargs ​

python
def total(*nums):          # 收成元组
    return sum(nums)

print(total(1, 2, 3))      # 6

def show(**info):          # 收成字典
    for k, v in info.items():
        print(k, "=", v)

show(name="小明", age=18)

调用时的 * 反过来用是解包:

python
nums = [1, 2, 3]
print(total(*nums))     # 等价于 total(1, 2, 3)

特殊参数 / 和 *(选读,写工具/库时再看) ​

看到 def f(a, /, b, *, c) 别慌,意思是:

  • / 前面的只能用位置传:f(1, b=2, c=3) ✅,f(a=1, b=2, c=3) ❌
  • * 后面的只能用关键字传:c=3 必须写名字,f(1, 2, 3) ❌

什么时候真的需要?当你写一个给别人用的函数,想明确禁止某种传参方式时。比如关键开关参数强制写名字,读代码的人一眼就知道它是什么:

python
CREATE_TABLE = True

def create_user(name, *, is_admin=False):
    ...

create_user("小明", is_admin=True)   # ✅ 一眼看懂
# create_user("小明", True)          # ❌ 报错,挡住歧义

自己写小脚本时用不到,看到别人的代码里出现 / * 知道含义即可。

4.4 返回多个值 ​

python
def min_max(nums):
    return min(nums), max(nums)      # 其实返回一个元组

low, high = min_max([3, 8, 1])
print(low, high)      # 1 8

4.5 lambda 与函数当作参数 ​

lambda 是"一次性小函数":

python
square = lambda x: x * x
print(square(5))       # 25

# 常见用途:排序时指定按什么排
students = [("小明", 92), ("小红", 88), ("小刚", 95)]
students.sort(key=lambda s: s[1], reverse=True)
print(students)        # [('小刚', 95), ('小明', 92), ('小红', 88)]

4.6 作用域:变量在哪儿有效 ​

python
count = 0            # 全局变量

def add_one():
    global count     # 声明"我要改外面那个 count"
    count += 1

add_one()
print(count)         # 1

规则很简单:函数里能读外面的变量,但想改外面的变量必须 global(实际写代码时尽量避免,靠参数传入、return 传出更清楚)。

python
def f():
    x = 10      # 局部变量,函数外访问不到

f()
# print(x)      # ❌ NameError

4.7 文档字符串与类型注解 ​

python
def area(width: float, height: float) -> float:
    """计算矩形面积。

    width: 宽
    height: 高
    返回:面积
    """
    return width * height

注解只是给人看的说明,不会强制检查,但编辑器能帮你提示错误。养成写类型注解的习惯,代码质量立刻上一个档次。

4.8 函数的递归(进阶了解) ​

函数自己调用自己,必须有"出口":

python
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))     # 120

递归能用循环替代,多数场景循环更直观。

4.9 编码风格三条(PEP 8) ​

  1. 函数名用 小写_下划线,望文知义(calc_tax 而不是 f2)
  2. 一个函数只干一件事;超过 30 行就该考虑拆开
  3. 运算符两边加空格:a = b + c

4.10 练一练 ​

  1. 写 is_prime(n) 判断质数,并打印 1–50 中的所有质数。
  2. 写 calc(nums) 返回列表的平均值、最大值、最小值三个值。
  3. 写 safe_int(s, default=0):能转成整数就返回整数,否则返回默认值(提示:用 try/except,见第 8 章;也可以先试着用字符串方法判断)。
  4. 把第 03 章的猜数字游戏改造成函数版本:guess_game(answer)。

本章关键词 ​

def、return、默认参数、*args / **kwargs、lambda、作用域 global、docstring、类型注解

试图用自己的方式,重新定义属于我的人生