函数是具有特定功能的、可重用的代码块。它将一系列相关的操作封装起来,通过一个简单的函数名来调用,从而实现代码的模块化和复用。例如,在数学计算中,计算一个数的平方根是一个常见的操作。我们可以定义一个名为“sqrt”的函数,当需要计算平方根时,直接调用这个函数,而不需要每次都重新编写计算平方根的代码。
一、函数基础
(一)定义
在Python中,定义函数使用def
关键字,其基本语法格式如下:
def 函数名(形参列表):
"""文档字符串""" # 可选,用于描述函数的功能和参数等信息
# 函数体
# 这里是一系列的代码,实现函数的功能
return 返回值 # 返回值是可选的
-
函数名:函数的名称,用于标识函数。函数名的命名规则遵循Python的标识符规则,通常使用小写字母和下划线组合,如
calculate_area
、print_message
等。函数名应该具有描述性,能够清晰地表达函数的功能。 -
形参列表:形参是函数定义时在括号中声明的参数,用于接收外部传入的数据。它可以有多个形参,形参之间用逗号分隔。例如,
def add(a, b):
中的a
和b
就是形参,它们在函数内部代表两个将要进行加法运算的数。形参可以有默认值,如def greet(name, age=18):
,这里的age
形参有一个默认值18。
(二)调用
函数调用是通过函数名和实际参数来执行函数的过程。其基本语法格式如下:
函数名(实参列表)
-
实参列表:实参是在调用函数时传递给函数的实际数据。实参的数量和类型需要与形参匹配。实参可以是具体的值,如数字、字符串等,也可以是变量、表达式等。
(三)形参
形参(形式参数)是在函数定义时声明的参数。它就像是函数内部的一个“占位符”,用于接收外部传入的数据。形参在函数被调用时才被赋予具体的值,这个值就是实参。
类型
-
位置形参:按照位置顺序接收实参的形参。这是最常见的形参类型。例如,在
def add(a, b):
中,a
和b
都是位置形参,它们按照实参的传递顺序接收数据。 -
关键字形参:通过关键字(形参名)来接收实参的形参。这种方式使得调用函数时可以不按照形参的顺序传递实参。例如,在
def greet(name, age):
中,调用greet(age=25, name="Alice")
时,name
和age
就是关键字形参。 -
默认形参:在函数定义时给形参赋予一个默认值的形参。当调用函数时,如果没有传递对应的实参,就使用这个默认值。例如,在
def greet(name, age=18):
中,age
是一个默认形参,如果调用greet("Bob")
时没有传递age
的实参,那么age
的值就是默认的18。 -
可变长形参:可以接收任意数量实参的形参。有两种可变长形参:
-
可变长位置形参:使用
*形参名
来定义,可以接收任意数量的位置实参,并将它们存储在一个元组中。例如,def sum_numbers(*nums):
,调用sum_numbers(1, 2, 3, 4)
时,nums
形参会接收到一个元组(1, 2, 3, 4)
。 -
可变长关键字形参:使用
**形参名
来定义,可以接收任意数量的关键字实参,并将它们存储在一个字典中。例如,def print_info(**info):
,调用print_info(name="Alice", age=25)
时,info
形参会接收到一个字典{"name": "Alice", "age": 25}
。
-
作用
-
数据传递:形参是函数与外部进行数据交互的桥梁。通过形参,函数可以获取外部传入的数据,从而根据这些数据执行相应的操作。例如,在一个计算圆面积的函数中,半径作为形参传入,函数根据这个半径值来计算面积。
-
代码复用:形参使得函数具有通用性。同一个函数可以接收不同的实参,执行相同的操作逻辑,但得到不同的结果。比如一个排序函数,它可以对不同的数组进行排序,只需要将不同的数组作为实参传入即可。
(四)实参
实参(实际参数)是在函数调用时传递给函数的实际数据。它是具体的数据值,用于给函数的形参赋值。
类型
-
位置实参:按照形参的位置顺序传递实参。这是最常见的实参类型。例如,在
def greet(name, age):
中,调用greet("Alice", 25)
时,"Alice"
和25
就是位置实参,它们分别对应形参name
和age
。 -
关键字实参:通过形参名来传递实参。这种方式可以不按照形参的顺序传递实参。例如,在
def greet(name, age):
中,调用greet(age=25, name="Alice")
时,age=25
和name="Alice"
就是关键字实参。关键字实参使得代码的可读性更好,尤其是在形参较多时。 -
可变长实参:传递给可变长形参的实参。对于可变长位置形参,可以传递任意数量的位置实参;对于可变长关键字形参,可以传递任意数量的关键字实参。例如,在
def sum_numbers(*nums):
中,调用sum_numbers(1, 2, 3, 4)
时,1, 2, 3, 4
就是可变长位置实参;在def print_info(**info):
中,调用print_info(name="Alice", age=25)
时,name="Alice", age=25
就是可变长关键字实参。
注意事项
-
位置实参和关键字实参混合使用:在调用函数时,位置实参必须在关键字实参之前。例如,
greet("Alice", age=25)
是正确的,但greet(name="Alice", 25)
是错误的。 -
可变长实参与其他实参混合使用:在定义函数时,可变长位置形参必须位于所有位置形参之后,可变长关键字形参必须位于所有形参(包括位置形参和关键字形参)之后。例如,
def func(a, b, *args, c, **kwargs):
是正确的形参列表顺序。
(五)返回值
返回值是函数执行完毕后返回给调用处的数据。它是一个函数的输出结果,可以是任何数据类型,如整数、浮点数、字符串、列表、元组、字典等。返回值使得函数可以将处理后的数据反馈给调用者,以便进行后续的操作。
基本语法
在Python中,使用return
语句来返回值。其基本语法格式如下:
return 返回值
-
返回值:可以是单个值,也可以是多个值。如果是多个值,Python会将它们打包成一个元组返回。例如,
return 1, 2, 3
等价于return (1, 2, 3)
。
示例
def get_user_info():
"""获取用户信息"""
name = "Alice"
age = 25
return name, age # 返回多个值
user_name, user_age = get_user_info()
print(user_name, user_age) # 输出Alice 25
在这个例子中,定义了一个名为get_user_info
的函数,它没有形参。函数体中定义了两个变量name
和age
,并使用return
语句返回这两个值。调用函数时,可以使用多个变量来接收返回的元组中的值。
二、函数的类型
(一)内置函数
Python自带的函数,无需用户定义,可以直接使用。例如:
-
print()
:输出函数,用于在控制台打印信息。 -
len()
:获取长度函数,用于获取字符串、列表等序列的长度。 -
type()
:获取类型函数,用于获取变量的数据类型。 -
int()
、float()
、str()
等:类型转换函数,用于将数据转换为对应的类型。
(二)自定义函数
用户根据自己的需求定义的函数。自定义函数的定义和使用方式在前面的“函数基础”部分已经详细介绍。
三、递归函数
(一)概念
递归函数是指在函数的定义中调用函数自身的一种函数。递归函数通常用于解决具有递归性质的问题,如计算阶乘、斐波那契数列等。
(二)特点
(三)示例
# 定义一个匿名函数,用于计算两个数的和
add = lambda x, y: x + y
# 调用匿名函数
result = add(3, 5)
print(result) # 输出8
# 使用匿名函数进行排序
numbers = [1, 3, 2, 4, 5]
sorted_numbers = sorted(numbers, key=lambda x: -x) # 按照降序排序
print(sorted_numbers) # 输出[5, 4, 3, 2, 1]
在这个例子中,定义了一个匿名函数add
,用于计算两个数的和。通过lambda x, y: x + y
定义了这个匿名函数,并将其赋值给变量add
。调用add(3, 5)
时,会返回8。
在排序示例中,使用sorted
函数对列表numbers
进行排序,并通过key=lambda x: -x
指定了排序的关键字函数。这个匿名函数用于计算每个元素的相反数,从而实现降序排序。
五、作用域
(一)概念
作用域是指变量、函数等标识符的有效范围。在Python中,主要有以下几种作用域:
(二)示例
# 全局变量
global_var = "I am global"
def outer_function():
# 外部函数的局部变量
outer_var = "I am outer"
def inner_function():
# 内部函数的局部变量
inner_var = "I am inner"
print(inner_var) # 访问内部函数的局部变量
print(outer_var) # 访问外部函数的局部变量
print(global_var) # 访问全局变量
inner_function()
outer_function()
在这个例子中,定义了一个全局变量global_var
,一个外部函数outer_function
和一个内部函数inner_function
。在inner_function
中,可以访问内部函数的局部变量inner_var
、外部函数的局部变量outer_var
和全局变量global_var
。这展示了不同作用域之间的访问关系。
六、闭包
(一)概念
闭包是指一个函数对象,它记录了创建它的作用域链。闭包可以捕获并记住外部函数的局部变量,即使外部函数已经执行完毕,这些变量仍然可以被内部函数访问。闭包在Python中通常通过嵌套函数实现。
(二)特点
在这个例子中,定义了一个性能测试装饰器performance_decorator
。performance_decorator
在调用被装饰的函数之前记录开始时间,在调用之后记录结束时间,并计算函数执行所花费的时间。通过@performance_decorator
语法,将装饰器应用到compute_sum
函数上。调用compute_sum(1000000)
时,会输出函数执行所花费的时间。
(三)权限校验
# 定义权限校验装饰器
def auth_decorator(func):
def wrapper(*args, **kwargs):
# 假设这里进行权限校验
authorized = True # 模拟权限校验结果
if not authorized:
raise Exception("Unauthorized access")
return func(*args, **kwargs)
return wrapper
# 使用权限校验装饰器
@auth_decorator
def admin_only_function():
print("This is an admin-only function.")
# 调用被装饰的函数
try:
admin_only_function()
except Exception as e:
print(e)
在这个例子中,定义了一个权限校验装饰器auth_decorator
。auth_decorator
在调用被装饰的函数之前进行权限校验,如果用户没有权限,则抛出异常。通过@auth_decorator
语法,将装饰器应用到admin_only_function
函数上。调用admin_only_function()
时,会先进行权限校验,如果校验不通过,则抛出“Unauthorized access”异常。
(三)示例
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
# 创建闭包
closure = outer_function(10)
# 调用闭包
result = closure(5)
print(result) # 输出15
在这个例子中,outer_function
是一个外部函数,它接受一个参数x
。内部函数inner_function
接受一个参数y
,并返回x + y
。当调用outer_function(10)
时,返回了inner_function
函数对象,这个函数对象就是一个闭包。闭包closure
捕获了外部函数的局部变量x
(值为10),即使outer_function
已经执行完毕,x
仍然可以被closure
访问。调用closure(5)
时,会返回15。
七、装饰器
(一)概念
装饰器是一种设计模式,用于在不修改原有函数代码的情况下,给函数添加新的功能。在Python中,装饰器通常是一个函数或类,它接受一个函数作为参数,并返回一个新的函数。装饰器可以用于日志记录、性能测试、事务处理、权限校验等场景。
(二)语法
函数装饰器
@装饰器函数名
def 被装饰的函数名():
# 函数体
类装饰器
@装饰器类名
class 被装饰的类名:
# 类体
(三)示例
函数装饰器示例
# 定义一个装饰器函数
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
# 使用装饰器
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
# 调用被装饰的函数
say_hello("Alice")
在这个例子中,定义了一个装饰器函数my_decorator
。my_decorator
接受一个函数func
作为参数,并返回一个新的函数wrapper
。wrapper
函数在调用func
之前和之后分别打印了一些信息。通过@my_decorator
语法,将装饰器应用到say_hello
函数上。调用say_hello("Alice")
时,会先执行wrapper
函数中的代码,然后调用say_hello
函数,最后再次执行wrapper
函数中的代码。
类装饰器示例
# 定义一个类装饰器
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Something is happening before the function is called.")
result = self.func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
# 使用类装饰器
@MyDecorator
def say_hello(name):
print(f"Hello, {name}!")
# 调用被装饰的函数
say_hello("Alice")
在这个例子中,定义了一个类装饰器MyDecorator
。MyDecorator
类的__init__
方法接受一个函数func
作为参数,并将其存储在实例变量中。__call__
方法使得类实例可以像函数一样被调用,在调用时执行一些额外的操作。通过@MyDecorator
语法,将类装饰器应用到say_hello
函数上。调用say_hello("Alice")
时,会先执行MyDecorator
实例的__call__
方法中的代码,然后调用say_hello
函数,最后再次执行__call__
方法中的代码。
八、装饰器案例
(一)日志记录
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
# 定义日志装饰器
def log_decorator(func):
def wrapper(*args, **kwargs):
logging.info(f"Calling function {func.__name__} with args {args} and kwargs {kwargs}")
result = func(*args, **kwargs)
logging.info(f"Function {func.__name__} returned {result}")
return result
return wrapper
# 使用日志装饰器
@log_decorator
def add(x, y):
return x + y
# 调用被装饰的函数
result = add(3, 5)
在这个例子中,定义了一个日志装饰器log_decorator
。log_decorator
在调用被装饰的函数之前和之后分别记录了日志信息,包括函数名、参数和返回值。通过@log_decorator
语法,将装饰器应用到add
函数上。调用add(3, 5)
时,会先记录调用日志,然后执行add
函数,最后记录返回值日志。
(二)性能测试
import time
# 定义性能测试装饰器
def performance_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time} seconds to execute.")
return result
return wrapper
# 使用性能测试装饰器
@performance_decorator
def compute_sum(n):
return sum(range(n))
# 调用被装饰的函数
result = compute_sum(1000000)
-
递归终止条件:递归函数必须有一个明确的终止条件,否则会导致无限递归,最终引发栈溢出错误。例如,在计算阶乘的递归函数中,当参数为1时,函数应该停止递归。
-
递归步骤:在每次递归调用中,问题的规模应该逐渐减小,逐步向终止条件靠近。
-
(三)示例
# 计算阶乘的递归函数 def factorial(n): if n == 1: # 递归终止条件 return 1 else: return n * factorial(n - 1) # 递归步骤 # 调用递归函数 result = factorial(5) print(result) # 输出120
在这个例子中,
factorial
函数用于计算阶乘。当n
等于1时,函数返回1,这是递归的终止条件。否则,函数会调用自身,计算n
乘以factorial(n - 1)
,每次调用都会使问题规模减小,直到达到终止条件。四、匿名函数
(一)概念
匿名函数是一种没有函数名的函数。在Python中,使用
lambda
关键字来定义匿名函数。匿名函数通常用于简单的操作,如排序、过滤等场景,可以避免定义一个完整的函数。(二)语法
lambda 参数列表: 表达式
-
参数列表:可以有多个参数,参数之间用逗号分隔。
-
表达式:是一个简单的表达式,用于计算返回值。匿名函数只能包含一个表达式,不能包含复杂的语句。
-
局部作用域:在函数内部定义的变量,只能在该函数内部访问。局部变量在函数调用时创建,在函数返回时销毁。
-
全局作用域:在模块(文件)级别定义的变量,可以在整个模块中访问。全局变量在模块加载时创建,在模块卸载时销毁。
-
嵌套作用域:当函数嵌套定义时,内部函数可以访问外部函数的变量,但外部函数不能访问内部函数的变量。
-
内置作用域:Python内置的变量和函数,如
print
、len
等,可以在任何地方访问。 -
捕获变量:闭包可以捕获外部函数的局部变量,并在内部函数中使用这些变量。
-
延长变量生命周期:闭包使得外部函数的局部变量的生命周期得以延长,直到闭包对象被销毁。
在这个例子中,定义了一个性能测试装饰器performance_decorator
。performance_decorator
在调用被装饰的函数之前记录开始时间,在调用之后记录结束时间,并计算函数执行所花费的时间。通过@performance_decorator
语法,将装饰器应用到compute_sum
函数上。调用compute_sum(1000000)
时,会输出函数执行所花费的时间。
(三)权限校验
# 定义权限校验装饰器
def auth_decorator(func):
def wrapper(*args, **kwargs):
# 假设这里进行权限校验
authorized = True # 模拟权限校验结果
if not authorized:
raise Exception("Unauthorized access")
return func(*args, **kwargs)
return wrapper
# 使用权限校验装饰器
@auth_decorator
def admin_only_function():
print("This is an admin-only function.")
# 调用被装饰的函数
try:
admin_only_function()
except Exception as e:
print(e)
在这个例子中,定义了一个权限校验装饰器auth_decorator
。auth_decorator
在调用被装饰的函数之前进行权限校验,如果用户没有权限,则抛出异常。通过@auth_decorator
语法,将装饰器应用到admin_only_function
函数上。调用admin_only_function()
时,会先进行权限校验,如果校验不通过,则抛出“Unauthorized access”异常。