【问题标题】:How is `x = 42; x = lambda: x` parsed?`x = 42; x = lambda: x` 已解析?
【发布时间】:2021-03-16 07:04:51
【问题描述】:

我很惊讶这个断言失败了:

x = 42
x = lambda: x
assert x() == 42

似乎x 最终递归引用了自己,因此x()x()() 等都是函数。

用来解析这个的规则是什么,这个记录在哪里?

顺便说一下(上面给出的并不意外),x 的原始值在 lambda 定义之后没有留下任何引用:

class X:
  def __del__(self): print('deleting')

x = X()
x = lambda: x  # 'deleting' is printed here

【问题讨论】:

  • 就像def x(): return x

标签: python language-lawyer


【解决方案1】:

变量x由第一个赋值创建,并与第二个赋值反弹。

由于 lambda 中的 x 在调用 lambda 之前不会计算,因此调用它将计算为最近分配的值。

请注意,这不是动态作用域——如果它是动态的,下面会打印“99”,但会打印“

x = 42
x = lambda: x

def test(f):
  x = 99
  print(f())

test(x)

【讨论】:

  • 所以这是词法(=静态)范围,对吗?同样相关:stackoverflow.com/a/51604390/336527.
  • 注意:使用nonlocal x; x = 99,它确实打印99。
  • 或者使用test(lambda: x) 来区分42 和99 可能更容易理解示例
【解决方案2】:

第一个赋值无关; lambda 正文中的 xbound late

x = lambda: x # no need for a prior assignment
x = lambda: y # notice: no NameError occurs, *until it is called*

这个道理和creating lambdas in a loop is tricky一样,也用来make trees with the standard library defaultdict

tree = lambda: defaultdict(tree)
t = tree()
t['foo']['bar']['baz'] = 'look ma, no intermediate steps'

【讨论】:

    【解决方案3】:

    lambda 是一个匿名函数对象。 Python 将等式右侧的所有内容完全解析为单个匿名对象,然后解析左侧的所有内容以进行赋值。

    x = lambda: x
    

    首先将lambda: x 编译成一个函数对象,该对象返回调用它时x 中发生的任何内容。然后它用这个函数对象重新绑定x,删除之前碰巧存在的任何对象。

    现在x 是一个函数,它返回x 中的任何内容...这是一个返回x 中的任何内容的函数,等等...所以您可以多次编写x()()()()()()想要,并且仍然得到那个原始的 lambda:x 函数对象。

    Python 函数有一个本地命名空间,但只有在函数中分配的变量驻留在那里。由于x 未在lambda 中分配,因此它在包含范围内解析 - 即模块级别“x”。一段相同的代码是

    def x():
        return x
    

    对比

    def x():
        x = 1
        return x
    

    现在,参数x 是一个局部变量,与全局x 无关。

    【讨论】:

      猜你喜欢
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 2013-05-06
      • 2018-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-24
      相关资源
      最近更新 更多