Should `list.extend` and `dict.update` be variadic?

This thought came up in the discussion thread for PEP 798 and I thought it might be worth some more discussion.

We can currently add elements from a whole bunch of iterables to a set by calling set.update with multiple arguments passed in:

>>> L = [[1,2,3], [], [4,5]]
>>> x = set()
>>> x.update(*L)
>>> x
{1, 2, 3, 4, 5}

However, list.extend and dict.update can’t be called in a similar way. For those, we would need a loop and multiple method calls to get the same result:

>>> L = [[1,2,3], [], [4,5]]
>>> x = []
>>> for list_ in L:
...     x.extend(list_)
...
>>> x
[1, 2, 3, 4, 5]
>>> L = [{1: 2}, {}, {3: 4, 1: 10}, [(9, 10)]]
>>> x = {}
>>> for dict_ in L:
...     x.update(dict_)
...
>>> x
{1: 10, 3: 4, 9: 10}

I think we should consider making list.extend and dict.update variadic as well, making them behave more like set.update and allowing code like the following:

>>> L = [[1,2,3], [], [4,5]]
>>> x = []
>>> x.extend(*L)
>>> x
[1, 2, 3, 4, 5]
>>> L = [{1: 2}, {}, {3: 4, 1: 10}, [(9, 10)]]
>>> x = {}
>>> x.update(*L)
>>> x
{1: 10, 3: 4, 9: 10}

I put together a quick proof-of-concept implementation of this idea, and an Emscripten-based demo in case anyone wants to play with this idea without needing to compile it yourself.

I’m curious what other people think.

11 Likes

They’re backwards compatible, and I can’t readily think of a way they would encourage subtly wrong code. I think this would be a nice improvement, as long as the semantics of dict.update(d, a, b, c) are the same as d.update({**a, **b, **c}) and d.update(a); d.update(b); d.update(c).

If this does not break backwards compatibility and there aren’t any other better ways to utilise this argument space, I am +1.

Also, if this is done, I think Sequence and Mapping would ideally adapt this as well.

This is not really possible :frowning:

There is no good way of evolving these interfaces.

1 Like

Forgive my ignorance, but could you elaborate? Is there more to this than just updating the MutableSequence and MutableMapping interfaces in collections.abc?

Yes. It also involves updating all existing implementations out in the wild to still be compatible with the new design, no matter if those implementations derive from the abc, are subclassing the corresponding typing Protocol or are implicitly implementing the Protocol.

If you don’t do this, code will no longer be able to properly interact and make assumptions about what it means to be a MutableMapping.

Obviously, this is not a feasible thing to do. Therefore there is no good way of evolving these interfaces.

4 Likes

Yep, this makes sense; the downstream effects would definitely be a big challenge :face_with_diagonal_mouth:. And changing list.extend and dict.update without similarly changing MutableSequence and MutableMapping would be painful as well.

That said, I do feel it’s a bit of a shame if the relationship between the built-in types and these interfaces means that any proposed changes to any of the associated methods are complete non-starters, especially for things like this that are backward-compatible.