通过walrus :=操作符进行多重赋值?

18
我曾尝试使用海豹运算符进行多次赋值,还看到了StackOverflow上的this等问题,也无法使用海豹运算符来赋值多个变量,我只是想知道成功的多重赋值应该是什么样子的,或者是否不可能。
这样做的目的是为我的库mvdef添加支持,以便在mvdef.src.ast_util模块的find_assigned_args函数中检测所有分配的变量名。
通过运行ast.parse,我可以看到:=运算符生成一个ast.NamedExpr节点,它具有一个.target属性,该属性是一个ast.Name对象,我可以从对象的.id属性中获取分配的名称。
如果必须猜测,我会认为如果可能的话,.target属性将是ast.Name对象的列表,而不是单个ast.Name对象,然而,我似乎无法得到这方面的例子,这让我怀疑它是否不可能,至少现在是这样(在这种情况下,我可以简化我的代码,不只是猜测实现应该是什么)。
如果有人知道在Python源代码中查找特定部分以告诉我这是否可能,那将非常有帮助,谢谢!顺便说一句,从查看通过initial commit提供的Lib/test/test_parser.py测试用例来看,似乎没有使用海象运算符进行多重赋值的示例,因此我暂时假设这是不可能的(但如果我错了,请发表意见!)
def test_named_expressions(self):
    self.check_suite("(a := 1)")
    self.check_suite("(a := a)")
    self.check_suite("if (match := pattern.search(data)) is None: pass")
    self.check_suite("[y := f(x), y**2, y**3]")
    self.check_suite("filtered_data = [y for x in data if (y := f(x)) is None]")
    self.check_suite("(y := f(x))")
    self.check_suite("y0 = (y1 := f(x))")
    self.check_suite("foo(x=(y := f(x)))")
    self.check_suite("def foo(answer=(p := 42)): pass")
    self.check_suite("def foo(answer: (p := 42) = 5): pass")
    self.check_suite("lambda: (x := 1)")
    self.check_suite("(x := lambda: 1)")
    self.check_suite("(x := lambda: (y := 1))")  # not in PEP
    self.check_suite("lambda line: (m := re.match(pattern, line)) and m.group(1)")
    self.check_suite("x = (y := 0)")
    self.check_suite("(z:=(y:=(x:=0)))")
    self.check_suite("(info := (name, phone, *rest))")
    self.check_suite("(x:=1,2)")
    self.check_suite("(total := total + tax)")
    self.check_suite("len(lines := f.readlines())")
    self.check_suite("foo(x := 3, cat='vector')")
    self.check_suite("foo(cat=(category := 'vector'))")
    self.check_suite("if any(len(longline := l) >= 100 for l in lines): print(longline)")
    self.check_suite(
        "if env_base := os.environ.get('PYTHONUSERBASE', None): return env_base"
    )
    self.check_suite(
        "if self._is_special and (ans := self._check_nans(context=context)): return ans"
    )
    self.check_suite("foo(b := 2, a=1)")
    self.check_suite("foo(b := 2, a=1)")
    self.check_suite("foo((b := 2), a=1)")
    self.check_suite("foo(c=(b := 2), a=1)")


:= 的左侧参数必须是标识符,不能是其他任何东西。如果你所说的“多重赋值”是指元组解包(a, b = ...),那么它是不被允许的。 - chepner
1
来自PEP-572:"这是形式为NAME:= expr的表达式,其中expr是除未加括号的元组之外的任何有效Python表达式,而NAME是标识符。" - chepner
1
同时在规范中需要注意,不支持多目标不是目录。我知道这不同于解构,但与@chepner的评论相符。 - Sunny Patel
啊,好的,感谢评论!如果有人想写一个答案,我很乐意接受并关闭。我确实是指“拆包”。 - Louis Maddox
1
关于允许在“海象”运算符的左侧进行元组解包,存在一个未解决的问题 - Sebastian Schrader
2个回答

23

可迭代对象的打包和解包是=:=之间的一个区别,只有前者支持它们。正如在PEP-572中所述:

# Equivalent needs extra parentheses
loc = x, y  # Use (loc := (x, y))
info = name, phone, *rest  # Use (info := (name, phone, *rest))

# No equivalent
px, py, pz = position
name, phone, email, *other_info = contact

有没有人知道其中的原因?如果它按照相同的方式工作,可能会更加直观,并且可以说语法会更简单,不是吗?只需重用具有不同优先级的规则即可。 - wihlke

-1
简短的回答是,您不能使用海象运算符(:=)进行多重赋值,但在我看来,这并不是一个重大缺点,因为有一种解决方法。
我们总是可以将返回多个值的函数包装在一个接受其余返回值作为输入参数的函数中。
考虑以下示例:
if not (userNameCapture := icm.subProc_bashOut(
        f"""grep Username: {plonePasswdFile} | cut -d ":" -f 2""",
        outcome=cmndOutcome,)): return icm.EH_badOutcome(cmndOutcome)

在这里,我将标准输出作为主要返回值得到,并且我在outcome中获得标准错误。

一切都很好,我们最终受益于海象运算符和多重赋值。


2
我看到了一个返回值,但没有定义,这应该怎么工作? - Spartan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接