Python 函数完全指南:定义与调用

目录

[1. 什么是函数?](#1. 什么是函数?)

[2. 函数的定义](#2. 函数的定义)

[2.1 基本语法](#2.1 基本语法)

[2.2 带参数的函数](#2.2 带参数的函数)

[2.3 带返回值的函数](#2.3 带返回值的函数)

[2.4 空函数(占位)](#2.4 空函数(占位))

[3. 函数的调用](#3. 函数的调用)

[4. 参数类型详解](#4. 参数类型详解)

[4.1 位置参数(必须参数)](#4.1 位置参数(必须参数))

[4.3 关键字参数](#4.3 关键字参数)

[4.4 可变长度参数](#4.4 可变长度参数)

[4.5 参数组合顺序](#4.5 参数组合顺序)

[5. 返回值详解](#5. 返回值详解)

[5.1 单个返回值](#5.1 单个返回值)

[5.2 多个返回值(本质是返回元组)](#5.2 多个返回值(本质是返回元组))

[5.3 return 语句省略时返回 None](#5.3 return 语句省略时返回 None)

[5.4 return 提前退出函数](#5.4 return 提前退出函数)

[6. 作用域](#6. 作用域)

[6.1 局部变量与全局变量](#6.1 局部变量与全局变量)

[6.2 nonlocal 关键字(用于嵌套函数)](#6.2 nonlocal 关键字(用于嵌套函数))

[7. 函数也是对象](#7. 函数也是对象)

[8. 匿名函数(lambda)](#8. 匿名函数(lambda))

[9. 递归函数](#9. 递归函数)

[10. 类型注解(Type Hints)](#10. 类型注解(Type Hints))

[11. 常见内置函数示例](#11. 常见内置函数示例)

12.常见陷阱与最佳实践


在编程中,我们经常需要重复执行某段代码。如果每次都复制粘贴,不仅代码冗长,而且修改起来非常麻烦。函数就是解决这个问题的利器:它将一段具有独立功能的代码打包成一个"积木块",需要时只需"调用"即可。

1. 什么是函数?

函数是一段可重复使用的代码块,它接收输入(参数),进行处理,并可能返回输出(返回值)。函数可以帮你:

  • 避免重复:同样的逻辑只需写一次。

  • 模块化:将复杂程序拆分成小功能块。

  • 易维护:修改函数内部实现,所有调用处自动生效。

Python 内置了很多函数(如 print()len()),你也可以自定义函数。

2. 函数的定义

2.1 基本语法

使用 def 关键字定义函数,后面跟函数名、括号 () 和冒号 :,函数体缩进。

def function_name(parameters):

"""可选的文档字符串"""

函数体代码

return 返回值 # 可选

eg

复制代码
def greet():
    """打印欢迎信息"""
    print("Hello, welcome to Python!")

# 调用函数
greet()
  • def greet():def 是定义函数的关键字,greet 是函数名,空括号表示该函数不接受任何参数,冒号表示函数体开始。

  • """打印欢迎信息""":这是一个文档字符串(docstring),用三个双引号包围。它是对函数功能的说明,可以通过 help(greet)greet.__doc__ 查看。虽然不是必需的,但强烈建议添加。

  • print("Hello, welcome to Python!"):这是函数体,缩进表示属于函数的一部分。当函数被调用时,这一行代码会被执行。

  • greet():调用函数,执行函数体内的代码。

2.2 带参数的函数

复制代码
def greet_person(name):
    """向指定的人打招呼"""
    print(f"Hello, {name}!")

greet_person("Alice")   # Hello, Alice!
  • def greet_person(name)::定义了一个形参 name,它将在函数内部作为变量使用。

  • print(f"Hello, {name}!"):函数体内使用了 name 变量,它的值由调用时传入。

  • greet_person("Alice"):调用时传入实参 "Alice",它会被赋值给形参 name,因此函数体内输出 Hello, Alice!

2.3 带返回值的函数

使用 return 语句返回结果。

复制代码
def add(a, b):
    """返回两个数的和"""
    result = a + b
    return result

sum_val = add(3, 5)   # 返回 8
print(sum_val)

代码解析

  • def add(a, b)::定义两个形参 ab

  • result = a + b:计算两个参数的和,赋值给局部变量 result

  • return result:将 result 的值返回给调用者。函数执行到 return 语句时会立即结束,并将返回值传递给调用方。

  • sum_val = add(3, 5):调用 add 函数,实参 35 分别传给 ab,函数返回 8,然后赋值给变量 sum_val

  • print(sum_val):输出 8

2.4 空函数(占位)

如果函数体还未实现,可以用 pass 占位。

复制代码
def not_ready():
    pass   # 稍后实现

代码解析

  • def not_ready()::定义函数。

  • pass:是一个空语句,什么都不做。语法上需要一个语句,但逻辑上还未实现,用 pass 避免语法错误。


3. 函数的调用

调用函数就是使用函数名加上括号和实际参数。

复制代码
# 定义
def multiply(x, y):
    return x * y

# 调用
result = multiply(4, 5)
print(result)   # 20

代码解析

  • def multiply(x, y)::定义乘法函数。

  • return x * y:返回两数乘积。

  • result = multiply(4, 5):调用函数,实参 4 对应 x5 对应 y,返回值 20 存入 result

  • print(result):输出 20

调用时的细节

  • 函数名后面必须跟括号,即使没有参数也要写 ()

  • 实参会按照形参的位置一一对应(位置参数)。

  • 调用时传递的实参个数必须与形参个数匹配(除非有默认参数)。

4. 参数类型详解

4.1 位置参数(必须参数)

最普通的形式,调用时按顺序传递。

复制代码
def greet_with_default(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet_with_default("Alice")           # Hello, Alice!
greet_with_default("Bob", "Hi")       # Hi, Bob!

代码解析

  • greeting="Hello":为 greeting 参数指定默认值 "Hello"。调用时若不提供第二个参数,则使用默认值。

  • greet_with_default("Alice"):只传一个参数,greeting 使用默认值 "Hello",输出 Hello, Alice!

  • greet_with_default("Bob", "Hi"):传入两个参数,greeting 被覆盖为 "Hi",输出 Hi, Bob!

注意:默认参数必须放在非默认参数之后。

复制代码
# 错误写法
def wrong(a=1, b):   # SyntaxError
    pass

陷阱:默认参数的值只在函数定义时计算一次,因此不能使用可变对象作为默认值(如列表)。

复制代码
# 不推荐
def add_item(item, lst=[]):
    lst.append(item)
    return lst

print(add_item(1))   # [1]
print(add_item(2))   # [1, 2]   ← 同一个列表被重复使用

代码解析

  • 因为默认参数 lst 在函数定义时被创建(一个空列表),之后每次调用若不提供 lst,都会使用同一个列表对象,导致累积。

  • 解决办法是使用 None 作为默认值,然后在函数内部创建新列表。

正确做法

复制代码
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

代码解析

  • lst=None:默认值设为不可变对象 None

  • if lst is None: lst = []:每次调用时若未传入列表,则新建一个空列表,保证了独立性。

4.3 关键字参数

调用时通过形参名指定实参,可以改变顺序。

复制代码
def describe_pet(name, species):
    print(f"{name} is a {species}")

describe_pet(species="cat", name="Tom")   # Tom is a cat

代码解析

  • describe_pet(species="cat", name="Tom"):显式指定每个参数的名字,顺序可以与定义不同。这样调用时不会受位置影响,增强了可读性。

4.4 可变长度参数

  • *args:接收任意多个位置参数,打包成元组。

  • **kwargs:接收任意多个关键字参数,打包成字典。

    def sum_all(*args):
    return sum(args)

    print(sum_all(1, 2, 3, 4)) # 10

代码解析

  • *args:收集所有传入的位置参数为一个元组,这里 args(1,2,3,4)

  • sum(args):内置函数 sum 对元组求和,返回 10

    def print_info(**kwargs):
    for key, value in kwargs.items():
    print(f"{key}: {value}")

    print_info(name="Alice", age=30, city="Paris")

代码解析

  • **kwargs:收集所有传入的关键字参数为一个字典,这里 kwargs{'name':'Alice','age':30,'city':'Paris'}

  • 遍历字典并打印。

4.5 参数组合顺序

在函数定义中,参数的顺序必须是:

  1. 位置参数

  2. 默认参数

  3. *args

  4. 关键字参数(仅限 * 之后)

  5. **kwargs

    def complex_func(a, b=1, *args, c=2, **kwargs):
    pass

代码解析

  • a 是普通位置参数。

  • b=1 是默认参数。

  • *args 接收多余的位置参数。

  • c=2 是仅限关键字参数(必须通过关键字传递)。

  • **kwargs 接收多余的关键字参数。


5. 返回值详解

5.1 单个返回值

复制代码
def square(x):
    return x ** 2

代码解析 :函数返回 x 的平方,返回后调用方可以接收。

5.2 多个返回值(本质是返回元组)

复制代码
def get_stats(numbers):
    return min(numbers), max(numbers), sum(numbers)/len(numbers)

low, high, avg = get_stats([1,2,3,4,5])
print(low, high, avg)   # 1 5 3.0

代码解析

  • return min(...), max(...), avg(...):实际上返回了一个元组 (min, max, avg)

  • 调用方通过 low, high, avg = ... 进行拆包,分别接收三个值。

5.3 return 语句省略时返回 None

复制代码
def do_nothing():
    pass

print(do_nothing())   # None

代码解析 :函数没有显式 return,默认返回 None

5.4 return 提前退出函数

复制代码
def is_positive(n):
    if n > 0:
        return True
    return False

代码解析

  • 如果 n > 0,执行 return True,函数立即结束,不会执行后面的 return False

  • 否则执行 return False。这种写法可避免使用 else


6. 作用域

6.1 局部变量与全局变量

  • 在函数内部赋值的变量是局部变量,只在函数内有效。

  • 在函数外部定义的变量是全局变量 ,可在函数内读取,但若要修改需用 global 关键字。

    x = 10 # 全局变量

    def func():
    global x # 声明要修改全局变量
    x = 20
    y = 5 # 局部变量

    func()
    print(x) # 20

代码解析

  • x = 10:全局变量。

  • def func(): 内部 global x:告诉 Python 这里的 x 是全局变量,而不是创建局部变量。

  • x = 20:修改全局 x 的值为 20。

  • y = 5:未声明 global,因此是局部变量,函数外部无法访问。

  • 调用 func() 后,全局 x 变为 20。

注意 :避免过多使用 global,推荐通过参数传递和返回值来通信。

6.2 nonlocal 关键字(用于嵌套函数)

在内层函数中修改外层函数(非全局)的变量。

复制代码
def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner

counter = outer()
print(counter())   # 1
print(counter())   # 2

代码解析

  • outer 函数中定义了局部变量 count 和内层函数 inner

  • inner 中使用 nonlocal count 声明要修改的是外层函数 outercount 变量,而不是创建一个新的局部变量。

  • count += 1 修改外层变量。

  • outer 返回 inner 函数对象,形成一个闭包。每次调用 counter() 都会增加并返回 count 的值。


7. 函数也是对象

在 Python 中,函数是一等公民,可以赋值给变量、作为参数传递、作为返回值。

复制代码
def add(a, b):
    return a + b

my_func = add       # 赋值
print(my_func(3, 4))   # 7

代码解析 :将函数 add 赋值给变量 my_funcmy_func 现在指向同一个函数对象,可以像 add 一样调用。

复制代码
def apply(func, x, y):
    return func(x, y)

print(apply(add, 5, 6))   # 11

代码解析apply 函数接收一个函数作为参数 func,然后在内部调用它。这体现了高阶函数的特性。


8. 匿名函数(lambda)

lambda 表达式用于创建简单的、单表达式的匿名函数。

复制代码
square = lambda x: x ** 2
print(square(5))   # 25

代码解析

  • lambda x: x ** 2 定义了一个匿名函数,等价于 def square(x): return x**2

  • lambda 只能包含一个表达式,不能包含语句。

    常用于 sort 的 key 参数

    pairs = [(1, 'one'), (2, 'two'), (3, 'three')]
    pairs.sort(key=lambda pair: pair[1]) # 按字符串排序

代码解析sortkey 参数接收一个函数,用于从每个元素中提取比较键。这里用 lambda 取每个元组的第二个元素(字符串)作为排序依据。

限制 :lambda 只能有一个表达式,不能包含语句(如 printreturn 等)。


9. 递归函数

函数调用自身称为递归。需要明确的终止条件。

复制代码
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))   # 120

代码解析

  • if n <= 1: return 1:递归的终止条件,防止无限递归。

  • return n * factorial(n-1):递归调用,每次减少 n 的值。

  • 调用过程:factorial(5)5 * factorial(4)5 * 4 * factorial(3) → ... → 5 * 4 * 3 * 2 * 1

注意:Python 递归深度有限制(默认约 1000),深层递归建议改用循环。

10. 类型注解(Type Hints)

Python 3.5+ 支持类型注解,提高代码可读性(不强制检查)。

复制代码
def greet(name: str) -> str:
    return f"Hello, {name}"

代码解析

  • name: str:表示参数 name 应为字符串类型。

  • -> str:表示返回值应为字符串类型。

  • 这些注解只是提示,不会在运行时检查类型错误。可以用 mypy 工具进行静态类型检查。


11. 常见内置函数示例

  • print():输出

  • len():返回长度

  • type():返回类型

  • input():读取用户输入

  • range():生成整数序列

  • sum()max()min()

这些内置函数无需定义,直接使用。

12.常见陷阱与最佳实践

陷阱 说明 解决方案
使用可变对象作为默认参数 函数多次调用共享同一对象 使用 None 并在函数内创建新对象
在函数内修改全局变量未声明 会创建同名局部变量 使用 global 或通过参数/返回值传递
混淆位置参数和关键字参数 调用时顺序错误 明确指定关键字参数名
递归深度过大导致栈溢出 递归超过系统限制 改用循环或增加 sys.setrecursionlimit
忘记 return 导致返回 None 预期有返回值但实际为 None 检查函数所有分支是否都有 return
lambda 内使用复杂逻辑 无法使用语句,代码难读 改用普通函数

感谢你的观看,期待我们下次再见!

相关推荐
曹牧1 小时前
Java:Unix时间戳
java·开发语言
大数据魔法师1 小时前
Streamlit(十二)- API 参考文档(五)- 输入组件
python·web
会编程的土豆1 小时前
Go 里的 error 接口 + 假 nil(超级重点)
开发语言·后端·golang
愿天垂怜1 小时前
【C++脚手架】ffmpeg 库的介绍与使用
linux·服务器·开发语言·c++·ide·git·ffmpeg
涛声依旧-底层原理研究所1 小时前
Node.js在高并发低延迟场景中的优势
java·人工智能·python·node.js
并不喜欢吃鱼1 小时前
从零开始 C++-----十一【C++ 数据结构】红黑树全解析:从定义到工程实现(一文搞定,十分详细)
开发语言·数据结构·c++
不会C语言的男孩1 小时前
C++ Primer Plus 第7章:函数——C++的编程模块
开发语言·c++
方也_arkling1 小时前
【Java-Day09】继承
java·开发语言
迈巴赫车主1 小时前
蓝桥杯21247弹跳鞋java
java·开发语言·数据结构·算法·职场和发展·蓝桥杯