3

I've attempted to make multiple assignments with the walrus operator, and seen questions on StackOverflow such as this which also fail to assign multiple variables using a walrus operator, and am just wondering what a successful multiple assignment would look like, or whether it is not possible.

The purpose of doing so is to add support for detecting all assigned variable names in my library mvdef (specifically, within the find_assigned_args function in the mvdef.src.ast_util module).

From running ast.parse I can see that the := operator produces an ast.NamedExpr node, and this has a .target attribute which is an ast.Name object, from which I can obtain the assigned name from the object's .id attribute.

If I had to guess, I'd presume that if it were to be at all possible, the .target attribute would be a list of ast.Name objects instead of a single ast.Name object, however the fact that I can't seem to get an example of this makes me wonder whether it is impossible, at least for the time being (in which case I can simplify my code and not just guess at what an implementation should be).

If anyone knows which specific part of the Python source to look at to tell me if this is possible or not, that'd be helpful, thanks!

P.S. - from looking at the test cases in Lib/test/test_parser.py provided in the initial commit (via), there don't seem to be examples of multiple assignments with the walrus operator, so I'm going to assume for now it's not possible (but please chime in if I'm wrong!)

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)")

martineau
  • 99,260
  • 22
  • 139
  • 249
Louis Maddox
  • 4,311
  • 5
  • 28
  • 59
  • The left-hand argument to `:=` must be an identifier, nothing else. If by "multiple assignment" you mean tuple unpacking (`a, b = ...`), it simply isn't allowed. – chepner Jan 02 '20 at 17:07
  • 1
    From [PEP-572](https://www.python.org/dev/peps/pep-0572/#id9): "This is of the form NAME := expr where expr is any valid Python expression other than an unparenthesized tuple, and **NAME is an identifier**." – chepner Jan 02 '20 at 17:08
  • 1
    Also note within the spec, that [multiple targets are not directory supported](https://www.python.org/dev/peps/pep-0572/#id14). I know this isn't the same as destructuring, but it goes along with @chepner 's comments. – Sunny Patel Jan 02 '20 at 17:14
  • Ah okay, thanks for the comments! If someone wants to write an answer I'm happy to accept and close. I did mean "unpacking" yes – Louis Maddox Jan 02 '20 at 17:17
  • 1
    There is an [open issue](https://bugs.python.org/issue43143) about allowing tuple unpacking on the left-hand side of the “walrus” operator. – Sebastian Schrader May 04 '21 at 08:18

1 Answers1

5

Iterable packing and unpacking is one difference between = and :=, with only the former supporting them. As found in 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
ParkerD
  • 755
  • 5
  • 12