【问题标题】:Using for loop to define multiple functions - Python使用 for 循环定义多个函数 - Python
【发布时间】:2018-04-03 13:20:42
【问题描述】:

假设我需要创建 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)

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

【问题讨论】:

  • 为什么你想要多个函数,在引用前一个函数时将每个返回值增加 1?这感觉像是一个 XY 问题。你用它做什么?也许有更好的解决方案适合您。
  • @MooingRawr 我现在正在学习通用运算符。当前的作业包括定义五个二元运算符(在 \C\times \Z 上),首先将整数表示为复数,然后在 \C 上使用适当的二元运算符。这导致了很多看起来相似的函数,所以我希望有一种方法可以缩短我的代码。

标签: python python-3.x functional-programming


【解决方案1】:

在这种情况下将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

说明

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

污染命名空间

来自 cmets,一个常见问题是:

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

为什么使用迭代器来定义函数是个坏主意?看起来 就像缩短代码给我的好方法。一般情况下更好吗 一一定义? ——杰西卡

我的简短回答:

使用globals().update 污染命名空间是个坏主意.. 持有 特制集合中的相关变量。这是有道理的 任何观点(可维护性、调用、修改等)。 – jpp

@BoarGules 的扩展答案:

这不是一个好主意,因为它动态创建函数,但是 它们只能由静态代码调用(否则代码不会 知道该叫什么),那么它们动态的意义何在? 动态创建它们会使代码难以阅读(你不能 轻松搜索函数定义)并让 IDE 变得不必要 麻烦在他们帮助你的努力中。您省去了繁琐、重复的工作 编码,但编程有时需要繁琐和细心 重复。努力和技巧最好花在加速 编码(聪明的搜索/替换,宏等)。 – BoarGules

【讨论】:

    【解决方案2】:

    函数通常是不可变的;但他们可能引用了数据。在你的 sn-p 中,这个引用有点不清楚,但 i 在函数体中只出现一次,作为读取。因此,它从某个外部范围读取,通常是您的 for 循环包含在其中的函数或模块。因为这恰好是一个共享上下文,所以每个addi 函数都会以相同的i 结束。

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

    说了这么多,尽管很奇怪,但仍然可以按照您的要求进行操作。这是一种方法:

    def addfunc(n):
        def addn(x):
            return x+n
        return addn
    
    for i in range(1,10):
        globals()['add{}'.format(i)] = addfunc(i)
    

    这滥用globals 将动态创建的名称注入模块的命名空间,并将这些函数中的每一个嵌套在另一个函数中,以创建包含各自n 值的命名空间。另一个经典的技巧是使用默认参数,operator.addpartial application 是一种更简洁的函数式风格。

    【讨论】:

    • 是的,我知道 addi 是如何在每次迭代中重新定义的,所以我的尝试将导致函数 addi 增加 10。我刚刚了解了字典,并没有'不知道他们可以这样使用,所以谢谢!
    【解决方案3】:

    解决方案:

    from types import FunctionType
    from copy import copy
    
    def copy_function(fn, name):
        return FunctionType(
        copy(fn.func_code),
        copy(fn.func_globals),
        name=name,
        argdefs=copy(fn.func_defaults),
        closure=copy(fn.func_closure)
    )
    
    for i in range(10):
        name = 'add' + str(i)
        def _add(x):
            return x + (i+1)
        globals()[name] = copy_function(_add, name)
    
    
    print add1(2) # 4
    print add3(8) # 12 
    

    使用来自https://stackoverflow.com/a/34796018/7529716的复制功能

    对于 Python3,将 copy_function 更改为:

    def copy_function(fn, name):
    
        return FunctionType(copy(fn.__code__), 
                            copy(fn.__globals__), name=name, argdefs=copy(fn.__defaults__),
                            closure=copy(fn.__closure__))
    

    【讨论】:

    • 不错的解决方案。我已经更新了 Python 3 的代码。
    【解决方案4】:

    这有帮助吗?

    def createFunc(i):
      def func(x):
        return x + (i+1)
      return func
    

    然后只需创建函数并将它们存储在数组中:

    add = [createFunc(i) for i in range(10)]
    print add[0](1)  # 1+(0+1) = 2
    print add[0](2)  # 2+(0+1) = 3
    print add[2](10) # 10+(2+1) = 13
    print add[2](11) # 11+(2+1) = 14
    

    【讨论】:

      【解决方案5】:

      正如其他人所指出的,您是否应该在生产代码中执行此操作是值得怀疑的。但是,出于教育目的,以下是通过生成函数名称并将它们注入全局命名空间的方法:

      >>> def f(x, i):
      ...     return x + i + 1
      ... 
      >>> from functools import partial
      >>> for i in range(10):
      ...     globals()['add%s' % i] = partial(f, i=i)
      ... 
      >>> add8(5)
      14
      >>> add3(5)
      9
      

      functools.partial 在 Python 中实现 currying 以创建单个函数定义的副本,其中每个副本“硬编码”一个或多个函数参数。

      【讨论】:

        猜你喜欢
        • 2020-03-14
        • 1970-01-01
        • 1970-01-01
        • 2012-11-06
        • 2020-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-29
        相关资源
        最近更新 更多