为了账号安全,请及时绑定邮箱和手机立即绑定

python 3 lambda 的参数绑定似乎已损坏

python 3 lambda 的参数绑定似乎已损坏

慕娘9325324 2023-07-18 14:59:56
我在 CentOS 7 环境中使用 Python 3.6.10。我正在尝试创建一个基于结构化规范执行的命令列表。将其视为 lambda 列表似乎很自然且符合 Python 风格。我通过遍历规范来构建 lambda 列表。令我惊讶的是,当我执行结果时,我发现每个 lambda 都是相同的,因为它在创建 lambda 时没有捕获其参数。我认为这是一个错误。以下是说明该行为的示例代码:specification = {    'labelOne': ['labelOne.one', 'labelOne.two', 'labelOne.three', 'labelOne.four', 'labelOne.five'],    'labelTwo': ['labelTwo.one', 'labelTwo.two', 'labelTwo.three', 'labelTwo.four', 'labelTwo.five'],    'labelThree': ['labelThree.one', 'labelThree.two', 'labelThree.three', 'labelThree.four', 'labelThree.five'],    'labelFour': ['labelFour.one', 'labelFour.two', 'labelFour.three', 'labelFour.four', 'labelFour.five'],    'labelFive': ['labelFive.one', 'labelFive.two', 'labelFive.three', 'labelFive.four', 'labelFive.five'],    }lambdas = []for label, labelStrings in specification.items():    for labelString in labelStrings:        lambdaString = f"""Label: \"{label}\" with labelString: \"{labelString}\""""        oneArgLambda = lambda someArg: print(someArg, lambdaString)        lambdas.append(oneArgLambda)for each in lambdas:    each('Show: ')我期望看到这个:Show:  Label: "labelOne" with labelString: "labelOne.one"Show:  Label: "labelOne" with labelString: "labelOne.two"Show:  Label: "labelOne" with labelString: "labelOne.three"Show:  Label: "labelOne" with labelString: "labelOne.four"Show:  Label: "labelOne" with labelString: "labelOne.five"Show:  Label: "labelTwo" with labelString: "labelTwo.one"Show:  Label: "labelTwo" with labelString: "labelTwo.two"Show:  Label: "labelTwo" with labelString: "labelTwo.three"Show:  Label: "labelTwo" with labelString: "labelTwo.four"lambda 的参数绑定是在执行 lambda 时发生的,而不是在创建 lambda 时发生的。这至少是出乎意料的,而且我认为可以说是错误的。我认为 lambda 尽管有局限性,但应该创建一个 CLOSURE —— 它的整个目的是捕获其参数在创建时的状态,以便稍后在计算 lambda 时可以使用它们。这就是为什么它被称为“闭包”,因为它在创建时关闭了其参数的值。我有什么误解吗?
查看完整描述

3 回答

?
德玛西亚99

TA贡献1770条经验 获得超3个赞

正如您所说,它创建了 CLOSURE,并且闭包在lambdaString的上部范围中使用指定变量,但技巧是所有 lambda 都使用对lambdaString的相同引用,并且因为每次它记住最后一个时都更改它。例如:


c = ['one', 'two']

res = []


for i in range(2):

    for y in c:

        def la(x):

            print(x, y)

        res.append(la)


for la in res:

    la('Show: ')

# Show:  two

# Show:  two

# Show:  two

# Show:  two

你只需要另一个关闭来防止这种情况


c = ['one', 'two']

res = []


for i in range(2):

    for y in c:

        def closure_y(y):

            def la(x):

                print(x, y)

            return la

            

        res.append(closure_y(y))


for la in res:

    la('Show: ')

# Show:  one

# Show:  two

# Show:  one 

# Show:  two

完整代码可能是


specification = {

    'labelOne': ['labelOne.one', 'labelOne.two', 'labelOne.three', 'labelOne.four', 'labelOne.five'],

    'labelTwo': ['labelTwo.one', 'labelTwo.two', 'labelTwo.three', 'labelTwo.four', 'labelTwo.five'],

    'labelThree': ['labelThree.one', 'labelThree.two', 'labelThree.three', 'labelThree.four', 'labelThree.five'],

    'labelFour': ['labelFour.one', 'labelFour.two', 'labelFour.three', 'labelFour.four', 'labelFour.five'],

    'labelFive': ['labelFive.one', 'labelFive.two', 'labelFive.three', 'labelFive.four', 'labelFive.five'],

    }


lambdas = []

for label, labelStrings in specification.items():

    for labelString in labelStrings:

        lambdaString = f"""Label: \"{label}\" with labelString: \"{labelString}\""""


        def clousure(lambdaString):

            oneArgLambda = lambda someArg: print(someArg, lambdaString)

            return oneArgLambda


        lambdas.append(clousure(lambdaString))


for each in lambdas:

    each('Show: ')


查看完整回答
反对 回复 2023-07-18
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

您还有一些其他评论和答案来解释正在发生的事情,并且您的问题“有趣”是因为它迫使读者对代码和各种绑定问题感到困惑。


但是,如果我在审查过程中从同事那里看到了您的代码,我会要求重写 - 不是因为我会立即知道存在错误,而是因为它需要太多的头脑来思考是否来自周围的绑定(并改变) 范围将完全按照希望的方式运行。


相反,坚持在你的程序中实行更严格的纪律,从而减轻你的读者(大多数时候谁是你)的认知负担。具体来说,将函数创建移至真正隔离的范围,并将所有不同的输入传递给该函数创建者。这种方法是可靠的,因为它要么在第一次尝试时起作用,要么完全失败(如果您忽略将所有需要的参数传递给函数创建者)。


一种方法是:


# A function to create another function, with non-surprising argument binding.

# We expect nothing from the surrounding scope. All business can be done locally.

def get_func(label, x):

    return lambda prefix: print(f'''{prefix} => {label}: {x}''')


# Some input data.

specification = {

    label : [label + str(n) for n in range(3)]

    for label in ('A', 'B', 'C')

}


# Use that data to create some functions.

funcs = [

    get_func(label, x)

    for label, xs in specification.items()

    for x in xs

]


# Run 'em.

for f in funcs:

    f('Show')


查看完整回答
反对 回复 2023-07-18
?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

这是一个替代方案


lambdas = []

for label, labelStrings in specification.items():

    for labelString in labelStrings:

        lambdaString = f"""Label: \"{label}\" with labelString: \"{labelString}\""""

        oneArgLambda = lambda someArg, lambdaString: print(someArg, lambdaString)

        lambdas.append((oneArgLambda, lambdaString))


for f, lambdaString in lambdas:

    f('Show: ', lambdaString)


查看完整回答
反对 回复 2023-07-18
  • 3 回答
  • 0 关注
  • 99 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信