如何在 Python 中在运行时定义函数
我们可以通过导入 types
模块并使用其 types.FunctionType()
函数定义 Python 函数,然后在运行时执行它,如下所示:
这段代码可以在 Python 命令提示符中正常工作。首先,我们导入 types
模块。然后,我们运行命令 dynf = ...;
,最后调用函数 dynf()
以获得如下所示的输出。
import types
dynf = types.FunctionType(compile('print("Really Works")', 'dyn.py', 'exec'), {})
dynf()
输出
Really Works
在许多情况下,在运行时定义 Python 函数都是很有用的,例如当您需要根据用户输入或配置设置动态创建函数时。在本节中,我将提供几个示例,介绍如何在 Python 中在运行时定义函数。
使用固定签名定义简单函数
在运行时定义函数的最简单方法是使用 lambda
关键字创建新的函数对象。以下是一个示例:
# 使用 lambda 在运行时定义函数
add = lambda a, b: a + b
# 调用该函数
result = add(1, 2)
print(result)
输出
3
在此示例中,我们使用 lambda
表达式定义了一个名为 add
的新函数,它接受两个参数 a
和 b
并返回它们的和。然后我们可以像调用其他函数一样调用该函数。
使用可变签名定义函数
如果您需要定义带有可变数量参数的函数,可以使用 *args
和 **kwargs
语法分别捕获可变长度的位置参数列表和关键字参数列表。以下是一个示例:
# 使用可变签名定义函数
def make_adder(*args, **kwargs):
def adder(x):
return sum(args) + sum(kwargs.values()) + x
return adder
# 在运行时定义新的函数
add_1 = make_adder(1)
add_2 = make_adder(2, 3, 4)
add_3 = make_adder(a=1, b=2, c=3)
# 调用这些函数
result_1 = add_1(10)
result_2 = add_2(10)
result_3 = add_3(10)
print(result_1)
print(result_2)
print(result_3)
输出
11
19
16
在此示例中,我们使用 *args
和 **kwargs
定义了一个高阶函数 make_adder
,它采用可变数量参数。该函数返回一个新函数 adder
,该函数采用单个参数 x
并返回其参数的总和以及 x
。
然后,我们通过使用不同的参数调用 make_adder
来在运行时定义几个新函数。每个函数都具有不同的签名,并在调用时表现不同。
使用闭包定义函数
闭包是一个在其定义时捕获其封闭作用域状态的函数。您可以使用闭包来定义访问其范围之外定义的变量的函数。以下是一个示例:
# 使用闭包定义函数
def make_multiplier(factor):
def multiplier(x):
return factor * x
return multiplier
# 在运行时定义新的函数
double = make_multiplier(2)
triple = make_multiplier(3)
# 调用这些函数
result_1 = double(10)
result_2 = triple(10)
print(result_1)
print(result_2)
输出
20
30
在此示例中,我们定义了一个名为 make_multiplier
的高阶函数,它接受单个参数 factor
。该函数返回一个名为 multiplier
的新函数,它接受单个参数 x
并返回 x
与 factor
的乘积。
然后,我们通过使用不同的参数调用 make_multiplier
来在运行时定义几个新函数。每个函数都捕获了其定义时 factor
的值,并在调用时使用该值。
使用动态名称定义函数
您还可以使用 globals()
或 locals()
函数将新函数更新到全局或本地命名空间中,在运行时使用动态名称定义函数。以下是一个示例:
# 使用动态名称定义函数
def make_function(name, x):
def function():
return x * x
globals()[name] = function
# 在运行时定义新的函数
make_function('square', 2)
# 调用该函数
result = square()
print(result)
输出
4
在此示例中,我们定义了一个高阶函数 make_function
,它接受两个参数:新函数的名称和值 x
。该函数定义了一个新函数 function
,它返回 x
的平方。
然后,我们使用 globals()
创建了一个名为 square
的新函数,并将其添加到全局命名空间中。
自定义签名定义函数
您还可以使用类型模块中的 types.FunctionType
类自定义签名在运行时定义函数。以下是一个示例:
import types
# 使用自定义签名定义函数
def make_function(name, args, body):
code = types.CodeType(
len(args), # 参数数量
0, # 关键字参数数量
0, # 局部变量数量
True, # 函数是一个生成器
False, # 函数不是异步的
False, # 函数没有可变参数
False, # 函数没有 kwonlyargs 参数
b'', # 没有默认参数值
(), # 没有 cell 变量
(), # 没有自由变量
name, # 函数名称
'', # 文件名称为空字符串
0, # 起始行号
b''.join(body) # 函数主体
)
return types.FunctionType(code, globals())
# 在运行时定义新的函数
name = 'double'
args = ['x']
body = [
b'return x * 2',
]
double = make_function(name, args, body)
# 调用该函数
result = double(10)
print(result)
输出
20
在此示例中,我们定义了一个高阶函数 make_function
,它接受三个参数:新函数的名称、参数列表和字节码指令列表。
该函数使用 types.CodeType
类创建一个具有指定参数计数、局部变量计数和函数主体的新代码对象。然后,它使用 types.FunctionType
类创建一个新函数对象,该对象使用生成的代码对象和全局命名空间。
然后,我们使用名称 'double'
、单个参数 'x'
和将 x
乘以 2 的函数主体调用 make_function
。这将创建一个名为 double
的新函数,其行为类似于内置的 double
函数。
使用装饰器定义函数
最后,您可以通过使用动态生成的装饰器函数装饰现有函数来在运行时定义函数。以下是一个示例:
# 定义一个添加日志记录的装饰器函数
def log_calls(func):
def wrapper(*args, **kwargs):
print(f'Calling function {func.__name__} with arguments {args} and {kwargs}')
return func(*args, **kwargs)
return wrapper
# 使用装饰器在运行时定义函数
@log_calls
def my_function(x):
return x * 2
# 调用该函数
result = my_function(10)
print(result)
输出
Calling function my_function with arguments (10,) and {}
20
在此示例中,我们定义了一个名为log_calls
的装饰器函数,它接受一个现有的函数,并返回一个新的函数 wrapper
,该函数记录其参数并调用原始函数。
然后,我们使用 @log_calls
装饰器语法将 my_function
函数装饰为 log_calls(my_function)
。这将创建一个名为 my_function
的新函数,该函数在每次调用时都会记录其参数。
最后,我们调用 my_function(10)
并打印结果。