使用for循环来定义多个函数 - Python

jes*_*ica 5 python functional-programming python-3.x

假设我需要创建10个名为add1,add2,...,add10的函数.对于每个i,addi接受整数x并输出x + i.

当我尝试以下内容并评估add2(10)时,Python给了我一个NameError.

for i in range(10):
    def addi(x):
        return x + (i+1)
Run Code Online (Sandbox Code Playgroud)

我知道上面的代码是错误的,因为我定义的addi函数不可变; 我很确定我所做的一切都是重新定义了10次.如何快速定义这10个功能?

jpp*_*jpp 7

使用functools.partial在这种情况下字典相结合。

我假设您真正想要做的更复杂,因为此特定任务不需要多个功能。

from functools import partial

def add(x, i):
    return x + i

d = {f'add{k}': partial(add, i=k) for k in range(1, 10)}

d['add3'](5)  # 8
Run Code Online (Sandbox Code Playgroud)

解释

  • 在专门定义的字典中存储可变数量的相关对象是一种很好的做法。
  • functools.partial 是一个高阶函数,它返回一个带有固定选定参数的输入函数。

污染命名空间

从评论中,一个常见的问题是:

OP 似乎在问是否有更好的方法来做def add1:……def add2:虽然我同意你的解决方案,但我不同意它是当前问题的解决方案这一事实。- MooingRawr

为什么使用迭代器来定义函数是个坏主意?对我来说,这似乎是一种缩短代码的好方法。一一定义通常更好吗?– 杰西卡

我的简短回答:

使用globals().update来污染命名空间是一个坏主意。将相关变量保存在特制的集合中。从任何角度(可维护性、调用、修改等)来看,它都是有意义的。– jpp

@BoarGules 的扩展答案:

这不是一个好主意,因为它动态创建函数,但它们只能由静态代码调用(否则代码将不知道该调用什么),那么它们动态的意义何在?动态创建它们会使代码难以阅读(您无法轻松搜索函数定义)并给 IDE 提供不必要的帮助来帮助您。您可以节省繁琐、重复的编码,但编程有时需要繁琐而仔细的重复。努力和技能最好花在加速编码上(聪明的搜索/替换、宏等等)。– 野猪格莱斯


Yan*_*ier 5

函数一般来说是不可变的;但他们可能引用了这样的数据。在您的代码片段中,此引用有点不清楚,但i仅在函数体中出现一次,作为读取。因此,它从某些外部范围读取,通常for是循环包含在其中的函数或模块。因为这恰好是一个共享上下文,所以每个addi函数最终都会得到相同的i.

另一个问题是,您在每次迭代中都使用该名称addi,并且该函数从未以其他名称出现。因此addi之前定义的任何函数都会丢失。这引出了第三个问题;为什么要动态创建名称(例如函数名称)?使用集合几乎总是更好,例如djpp 答案中的字典。否则,什么代码会引用您创建的函数?

话虽如此,尽管很奇怪,但仍然可以按照您的要求进行操作。这是一种方法:

def addfunc(n):
    def addn(x):
        return x+n
    return addn

for i in range(1,10):
    globals()['add{}'.format(i)] = addfunc(i)
Run Code Online (Sandbox Code Playgroud)

这滥用了globals将动态创建的名称注入到模块的名称空间中,并将每个函数嵌套在另一个函数中以创建保存其各自n值的名称空间。另一个经典的技巧是使用默认参数,并且部分应用operator.add一种更简洁的函数式风格。