函数的引用和调用
文章目录
前言
我们学习Python过程中想必听说过"函数是一等公民"
这句话,那么这句话到底是什么意思呢.
其实就是说,函数和其他常见的数据类型(像整数、字符串、列表等)拥有同等地位,可以像其他对象一样被引用、传递和操作。
例如:
- 赋值给变量(如
greeting = greet
) - 存储在数据结构(列表、字典等)
- 作为参数传递给其他函数(高阶函数)
- 作为函数返回值(动态生成函数)
理解函数的引用
与调用
的区别是掌握 Python 函数式编程的基础。
一、 知识点详解
函数的引用
VS 函数的调用
1.1 基本概念区分
-
函数引用
函数引用是指获取函数对象本身而不执行它
。当你只使用函数名而不加括号时,就是在引用函数。def greet(): print("Hello, Python!") func_ref = greet # 函数赋值给变量,变量保持函数对象的引用 print(func_ref) # <function greet at 0x000001BE1863B880> (函数对象的字符串表示形式)
-
函数调用
函数调用是指实际执行函数
,通过在函数名后加括号()
实现,可以传递参数。# 函数调用,执行函数体代码 greet() # Hello, Python!
1.2 函数引用详解
-
在 Python 中,函数作为对象,可以:
赋值给变量
存储在数据结构中
作为参数传递给其他函数
作为其他函数的返回值say_hello = greet say_hello() # Hello, Python! func_list = [greet, str.upper, len] func_list[0]() # Hello, Python! def call_func(f): f() call_func(greet) # Hello, Python!
-
函数引用的特性
引用函数不会执行函数体
多个引用指向同一个函数对象
可以通过引用调用函数print(greet is say_hello) # 输出: True
1.3 函数调用详解
-
基本调用形式
def say_hello(): print("Hello, Python!") say_hello() # 直接调用 def add(a, b): return a + b result = add(3, 5) # 调用函数并把返回值赋值给变量
-
调用操作的本质
当使用()
调用函数时,Python 会:
1. 查找函数名对应的函数对象
2. 创建一个新的栈帧(栈帧是函数执行时的上下文,包含函数的局部变量、参数等信息)用于函数执行
3. 执行函数体代码
4. 返回结果(如果有return
语句)
1.4 对比总结表
特性 | 函数引用 | 函数调用 |
---|---|---|
语法 | 只使用函数名(如 func ) | 函数名加括号(如 func() ) |
效果 | 获取函数对象本身 | 执行函数体代码 |
返回值 | 返回函数对象 | 返回函数 return 的值或 None |
使用场景 | 传递函数、装饰器、回调等 | 需要执行函数逻辑时 |
性能 | 开销小 | 有调用开销 |
是否立即执行 | 否 | 是 |
二、说明示例
示例1:基础用法
def greet():
print("Hello, World!")
# 函数引用
func_ref = greet # 仅获取函数对象
print(func_ref) # 输出: <function greet at 0x...>
# 函数调用
result = greet() # 输出: Hello, World!
print(result) # 输出: None(无返回值)
示例2:高阶函数应用
def square(x):
return x ** 2
# 正确用法(传递函数引用)
numbers = [1, 2, 3]
squared = list(map(square, numbers)) # ✅ square作为引用传递
print(squared) # [1, 4, 9]
# 错误用法(错误调用)
# 这里 square() 会立即执行函数,但 square 函数需要一个参数,而这里没有提供,所以会报错
# 并且 map 函数需要接收一个函数对象,而不是函数的返回值
try:
wrong = list(map(square(), numbers)) # ❌ square()立即执行,但因缺少参数导致错误
except TypeError as e:
print(f"Error: {e}") # 输出 Error: square() missing 1 required positional argument: 'x'
三、知识点总结
-
函数是一等公民 :
可赋值给变量、存入数据结构、作为参数或返回值 -
函数的引用 :
“指向” 函数的操作(不执行),只是获取函数对象 -
函数的调用 :
“执行” 函数的操作(触发逻辑),执行函数的代码并获取返回值
四、知识扩展
4.1 内存模型对比
操作 | 内存行为 |
---|---|
函数引用 | 获取函数对象的内存地址 |
函数调用 | 创建栈帧(分配临时内存) |
4.2 高阶应用场景
场景1:装饰器中的引用
def logger(func): # 接收函数引用
def wrapper(*args):
print(f"Calling {func.__name__}")
return func(*args) # 实际调用
return wrapper
@logger # 引用被装饰函数
def calculate(x, y):
return x * y
print(calculate(3, 4)) # 调用装饰后的函数
# 输出:
# Calling calculate
# 12
场景2:回调函数机制
# 定义一个模拟任务的函数,接受一个回调函数作为参数
def task(callback):
print("任务开始执行...")
# 模拟一些任务操作
result = 42
# 任务完成后调用回调函数,并传递结果
callback(result)
# 定义回调函数
def handle_result(result):
print(f"任务执行完成,结果是: {result}")
# 传递函数引用
task(handle_result)
4.3 常见误区
错误案例1:意外调用
def process_data():
return "Data processed"
# 错误:忘记调用函数
result = process_data # 引用函数而非调用
print(result) # 输出函数对象地址,而非处理结果
错误案例2:错误传递参数
def welcome_message():
return "欢迎光临!"
# 错误做法:将函数调用结果存入字典
message_dict = {
"welcome": welcome_message() # 存储固定结果 "欢迎光临!"
}
# 尝试再次获取欢迎语,因为之前存的是结果,无法动态更新
new_message = message_dict["welcome"]
print(new_message) # 欢迎光临!
# 为演示效果,重新定义函数
def welcome_message():
return "欢迎您的到来!"
# 再次获取欢迎语,结果不会改变
new_message = message_dict["welcome"]
print(new_message) # 输出: 欢迎光临! 而不是 "欢迎您的到来!"
五、知识点考察题
def interact_gen():
while True:
x = yield
print(x)
x = lambda: 'HI'
gen = interact_gen()
gen.send(None)
gen.send(x)
运行以上代码,输出结果可能是什么( )
- A.
HI
- B.
报错
- C.
<function <lambda> at 0x000002213A6513A0>
- D.
None <function <lambda> at 0x000002213A6513A0
答案:C
解析:
interact_gen
是生成器函数,通过yield
接收外部发送的值。gen.send(None)
启动生成器,使其停在x = yield
处等待接收值。gen.send(x)
向生成器发送 函数对象x
(即 lambda 函数本身),而非调用x()
的结果。- 生成器接收到
x
后,执行print(x)
,输出函数对象的字符串表示形式(如<function <lambda> at 内存地址>
),
对应选项 C。
关键点:
- 函数引用(
x
)与函数调用(x()
)的区别:前者传递函数对象,后者传递函数返回值。 - 生成器
send
方法传递的是对象本身,而非执行结果。