在 for 循环中创建的 lambda 函数的 Python 列表在具有不同 id [duplicate] 时行为相同

Python list of lambda functions created in a for loop behave the same while having a different id [duplicate]

提问人:Plaikeeaan 提问时间:8/7/2022 更新时间:8/7/2022 访问量:128

问:

我正在尝试解决经典的 FizzBuzz 问题


  • 打印从 1 到 N 的所有数字
    • 但是,如果能被 3 整除,则提示“嘶嘶声”
    • 但是,如果能被 5 整除,则提示“Buzz”
      • 但是,如果能被 3 和 5 整除,则提示“FizzBuzz”
    • 但是,如果能被 m 整除,那么等等......

在 Python 中以很多奇特的方式。其中之一是从字典中创建一个函数列表,以应用于 N 个数字。但是我遇到了一个意想不到的行为。我在 for 循环中创建了一个包含 3 个 lambda 函数的列表。它们的 id 不同,但它们都表现为最后创建的函数。我知道处理列表中的可变对象可能很棘手,但我无法理解这种行为。最好的方法是向你展示代码:

data = {3: "Fizz",
        5: "Buzz",
        7: "Bazz",}

def closures_on_dict(dictionnaire):
    """Create a list of functions x->f(x) from a dictionary
    The int key being a divisor, the string value being the prompted value
    if x is divisible by the divisor"""
    functions = list()
    ii = 0
    for divisor, sentance in dictionnaire.items():
        print(f"divisor id: {(divisor)}, sentance id: {id(sentance)}")
        functions += [lambda x: sentance if x%divisor == 0 else x]
        print(f"function id: {id(functions[ii])}")
        ii +=1
    return functions

###############################################################################    

if __name__=="__main__":

    functions = closures_on_dict(data)
    for ii, function in enumerate(functions):
        print(f"function[{ii}] id is: {id(function)}")
        for test in data.keys():
            print(f"\t function[{ii}]({test}) = {function(test)}")

结果是......

divisor id: 3, sentance id: 140444073732784
function id: 140444096066176
divisor id: 5, sentance id: 140444073732016
function id: 140444096066752
divisor id: 7, sentance id: 140444072258672
function id: 140444096065600

function[0] id is: 140444096066176
     function[0](3) = 3
     function[0](5) = 5
     function[0](7) = Bazz
function[1] id is: 140444096066752
     function[1](3) = 3
     function[1](5) = 5
     function[1](7) = Bazz
function[2] id is: 140444096065600
     function[2](3) = 3
     function[2](5) = 5
     function[2](7) = Bazz

所以他们的 ID 不同,但它们的工作方式都一样。显然有一个简单的解决方法:

def dirty_patch(key, value):
    return lambda x: value if x%key == 0 else x

然后它完美地工作:

function[0] id is: 140444096048576
     function[0](3) = Fizz
     function[0](5) = 5
     function[0](7) = 7
function[1] id is: 140444096049008
     function[1](3) = 3
     function[1](5) = Buzz
     function[1](7) = 7
function[2] id is: 140444096217440
     function[2](3) = 3
     function[2](5) = 5
     function[2](7) = Bazz

但是我想知道为什么当我在 closures_on_dict() 创建 lambda 函数时,即使具有不同的 id,它们的行为也相同?我认为它与 for 循环中的可变参数“除数”和“句子”有关,但我在 Python 方面不够熟练,无法理解问题出在哪里。

列表 λ 闭 包 可变

评论

1赞 juanpa.arrivillaga 8/7/2022
简而言之,当我这样做时,你期望什么?def foo(): print(x)x = 2; foo()x =22; foo()
0赞 Plaikeeaan 8/7/2022
在阅读建议的答案之前,我会假设脚本会停止,因为没有定义,但现在我明白了。谢谢。但对于一种语言的概念来说,这似乎是一个奇怪的选择......x
0赞 juanpa.arrivillaga 8/7/2022
一点也不奇怪,这些语义,即词法范围的闭包,实际上很常见。几乎每种语言都有这些完全相同的语义。实际上,您可以先定义,因为这不是相关部分,因此我们可以将其从考虑中删除。x

答: 暂无答案