Python 函数是编程的核心,它通过 def关键字将代码封装为可重用的模块,大幅提升代码的清晰度和维护性。
函数的定义与调用
在 Python 中,我们使用 def关键字来定义函数。一个典型的函数结构如下:
def greet(name): # `greet` 是函数名,`name` 是参数
"""
向指定的人打招呼。 # 这是文档字符串 (Docstring),用于说明函数用途
"""
message = f"Hello, {name}!"# 函数体:具体的执行逻辑
return message # 使用 return 语句返回结果
# 调用函数,'Alice' 是实际传递给参数 `name` 的值(实参)
result = greet('Alice')
print(result) # 输出:Hello, Alice!
- 定义与调用:函数需要先定义,后调用。定义函数相当于制造工具,而调用函数才是使用工具。
- 返回值:return是函数结束的标志,它会将结果返回给调用者。如果函数没有 return语句,它会自动返回 None。
参数传递
- 位置传参
这是最直观的方式。调用函数时,传入的实参会按照位置顺序依次赋值给形参。
def introduce(name, age, city):
print(f"大家好,我叫{name},今年{age}岁,来自{city}。")
introduce("张三", 25, "北京")# 输出:大家好,我叫张三,今年25岁,来自北京。
在这里,”张三”传给 name,25传给 age,”北京”传给 city。顺序至关重大。
- 关键字传参
顺序虽然打乱,但通过关键字准确匹配。关键字参数必须放在所有位置参数之后,例如 introduce(“王五”, city=”广州”, age=28)是合法的,但 introduce(name=”王五”, 28, “广州”)会导致语法错误。
通过 explicitly 指定参数名来传递值,这种方式不依赖于参数的位置,使得代码更清晰,尤其在参数众多时。
def introduce(name, age, city):
print(f"大家好,我叫{name},今年{age}岁,来自{city}。")
introduce(age=30, city="上海", name="李四") # 输出:大家好,我叫李四,今年30岁,来自上海。
- 默认参数定义时,带有默认值的参数必须放在所有非默认参数(位置参数)的后面。
在定义函数时,可以为某些参数指定默认值。这样在调用函数时,如果省略这些参数,就会使用其默认值。这极大地增加了函数的灵活性。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # 输出:Hello, Alice! (使用默认的greeting)
greet("Bob", "Hi") # 输出:Hi, Bob! (覆盖默认的greeting)
- 可变位置参数 (args)
在形参前加一个 *,如 *args(名称习惯用 args,但关键是 *)。它会将调用时传入的所有多余的位置参数收集到一个元组中。
def sum_numbers(*args):
total = 0
for num in args:
total += num
return total
print(sum_numbers(1, 2, 3)) # 输出:6
print(sum_numbers(10, 20, 30, 40)) # 输出:100
- 可变关键字参数 (*kwargs)
在形参前加两个 *,如 **kwargs(名称习惯用 kwargs)。它会将调用时传入的所有多余的关键字参数(即未在形参列表中定义的键值对)收集到一个字典中。
def print_profile(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_profile(name="Charlie", age=35, occupation="Engineer")
# 输出:
# name: Charlie
# age: 35
# occupation: Engineer
- 参数混合使用与顺序规则强制顺序规则为:位置参数 -> 默认参数 -> *args -> 关键字参数(或称keyword-only参数,如d和e) -> **kwargs。
以上几种方式可以混合在一个函数定义中,但必须遵循严格的顺序,否则Python解释器无法正确解析:
def complex_example(a, b, c=10, *args, d, e=20, **kwargs):
print(f"a={a}, b={b}, c={c}")
print(f"args tuple: {args}")
print(f"d={d}, e={e}")
print(f"kwargs dict: {kwargs}")
# 调用示例 complex_example(1, 2, 3, 4, 5, d=6, f=7, g=8)
# 输出:
# a=1, b=2, c=3
# args tuple: (4, 5) # 位置参数a,b,c分配后,剩下的4和5给了args
# d=6, e=20 # d必须用关键字指定,e使用了默认值
# kwargs dict: {'f': 7, 'g': 8} # 多余的关键字参数
传参的“值”与“引用”
这是一个重大致念,它决定了函数内部对参数的操作是否会影响到函数外部的原始变量。
- 传递不可变对象(值传递/传值):当传入的实参是不可变对象,如整数、字符串、元组时,函数内部对形参的任何修改(如重新赋值)都只发生在函数内部,不会影响外部的实参。这相当于传递了一个副本。
def try_change_immutable(x):
x = x + 10 # 尝试修改
print("函数内 x =", x)
num = 5
try_change_immutable(num) # 输出:函数内 x = 15
print("函数外 num =", num) # 输出:函数外 num = 5 (未改变)
- 传递可变对象(引用传递/传引用):当传入的实参是可变对象,如列表、字典时,传递的是指向该对象的“引用”。函数内部对形参内容的修改(如修改列表元素、向字典添加键值对)会直接影响外部的实参。如果不想改变原列表,可以在传参时传递一个副本,例如使用切片 function_name(my_list[:])或 list(my_list)。
def try_change_mutable(my_list):
my_list.append(4) # 修改列表内容
print("函数内 my_list =", my_list)
lst = [1, 2, 3]
try_change_mutable(lst) # 输出:函数内 my_list = [1, 2, 3, 4]
print("函数外 lst =", lst) # 输出:函数外 lst = [1, 2, 3, 4] (已被改变)
变量作用域
在函数内部定义的变量属于局部作用域,只在函数内部有效。而在函数外部定义的变量则属于全局作用域。
- 局部变量:在函数内创建,函数执行完毕后会被销毁,外部无法访问。
- 全局变量:在函数外创建,可以在整个程序(包括函数内部)被访问。
- 修改全局变量:如果需要在函数内部修改全局变量,需要使用 global关键字进行声明。
count = 0 # 全局变量
def increment():
global count # 声明要修改全局变量 count
count += 1
local_var = "inside" # 局部变量,函数外部无法访问
increment()
print(count) # 输出: 1
# print(local_var) # 这行会报错,由于 local_var 未定义
高阶用法
除了基础用法,函数还有一些强劲的高级特性:
- Lambda 匿名函数:用于定义简单的、单行就能实现的函数,使用 lambda关键字。
square = lambda x: x ** 2 # 一个计算平方的匿名函数
print(square(5)) # 输出: 25
- 函数作为参数与返回值(高阶函数):函数本身也可以作为值传递给另一个函数,或者作为另一个函数的返回值。这是函数式编程的基础。
def apply_operation(x, y, operation):
return operation(x, y)
result = apply_operation(10, 5, lambda a, b: a + b) # 传递一个加法函数
print(result) # 输出: 15
- 闭包
闭包的本质是函数和与其相关的引用环境的组合。当内部函数引用了外部函数的变量时,Python 会将这些变量(自由变量)绑定到内部函数上,形成一个闭包。即使外部函数执行完毕,其局部作用域本应销毁,但由于闭包的存在,这些被引用的变量会继续存活,供内部函数后续使用 。
一个简单的例子能清晰地展示这一点:
def outer_function(x): # 外部函数
def inner_function(y): # 内部函数
return x + y # 内部函数引用了外部函数的变量x(自由变量)
return inner_function # 外部函数返回内部函数
# 创建闭包实例,此时outer_function已执行完毕,但x=10被记住了
closure = outer_function(10)
print(closure(5)) # 输出 15 (10 + 5)
print(closure(20)) # 输出 30 (10 + 20)
在这个例子中,inner_function就是一个闭包。它捕获并记住了外部函数outer_function的参数x=10。之后每次调用closure,它都能使用这个记住的x的值 。
- 装饰器:一种强劲的工具,允许你在不修改原函数代码的情况下,为其增加新的功能。它本质上利用了函数可以作为参数和返回值的特性。装饰器是 Python 中超级强劲的语法糖,其底层实现是闭包。
def my_decorator(func):
def wrapper():
print("函数执行前...")
func()
print("函数执行后...")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 函数执行前...
# Hello!
# 函数执行后...
暂无评论内容