Oh, that's quite different than mapping patterns in PEP 634. :-(

On Thu, Oct 22, 2020 at 8:28 PM Steven D'Aprano <st...@pearwood.info> wrote:

> I would like to propose dict (or mapping) unpacking assignment. This is
> inspired in part by the Python-Ideas thread "f-strings as assignment
> targets", dict unpacking in function calls, and iterable unpacking
> assignment.
>
> Comments welcome.
>
>
> Background
> ----------
>
> Iterable unpacking assignment:
>
>     values = (1, 2, 3)
>     a, b, c = *values
>
> is a very successful and powerful technique in Python. Likewise dict
> unpacking in function calls and dict displays:
>
>     kwargs = {'a': 1, 'b': 2}
>     func(**kwargs)
>     d = {**kwargs, 'c': 3}
>
>
> There have been various requests for allowing dict unpacking on the
> right-hand side of an assignment [citation required] but in my opinion
> none of them have had a good justification.
>
> Motivated by the idea of scanning text strings with a scanf-style
> function, I propose the following behaviour for dict unpacking
> assignment:
>
>     items = {'eggs': 2, 'cheese': 3, 'spam': 1}
>     spam, eggs, cheese = **items
>     assert spam == 1 and eggs == 2 and cheese == 3
>
>
> Syntax
> ------
>
>     target_list [, **target] = **expression
>
> `target_list` is a comma-separated list of targets. Targets may be:
>
>
> - simple names, e.g. `spam` and `eggs`
>
> - dotted names, e.g. `spam.eggs`
>
> - numbered subscripts, e.g. `spam[1]`
>
>
> but is not required to support arbitrary complex targets such as:
>
>     spam(*args).eggs(2*x + y)[1].cheese  # not supported
>
> Likewise only int literals are supported for subscripts. (These
> restrictions may be lifted.)
>
> This is similar to the limited range of fields acceptabled by the string
> format mini-language. The same restrictions apply to `**target`.
>
> Each target must be unique.
>
> `expression` must evaluate to a dict or other mapping.
>
> Assignment proceeds by matching up targets from the left to keys on the
> right:
>
> 1. Every target must be matched exactly by a key. If there is a target
> without a corresponding key, that is an error.
>
> 2. Any key which does not match up to a target is an error, unless a
> `**target` is given.
>
> 3. If `**target` is given, it will collect any excess key:value pairs
> remaining into a dict.
>
> 4. If the targets and keys match up, then the bindings are applied from
> left to right, binding the target to the value associated with that key.
>
> Examples:
>
>
>     # Targets are not unique
>     a, b, a = **items
>     => SyntaxError
>
>     # Too many targets
>     a, b, c = **{'a': 1, 'b': 2}
>     => raises a runtime exception
>
>     # Too few targets
>     a = **{'a': 1, 'b': 2}
>     => raises a runtime exception
>
>     a, **extras = **{'a': 1, 'b': 2}
>     assert a == 1
>     assert extras == {'b': 2}
>
>     # Equal targets and keys
>     a, b, **extras = **{'a': 1, 'b': 2}
>     assert a == 1
>     assert b == 2
>     assert extras == {}
>
>     # Dotted names
>     from types import SimpleNamespace
>     obj = SimpleNamespace()
>     obj.spam = **{'obj.spam': 1}
>     assert obj.spam == 1
>
>     # Subscripts
>     arr = [None]*5
>     arr[1], arr[3] = **{'arr[3]': 33, 'arr[1]': 11}
>     assert arr == [None, 11, None, 33, None]
>
>
> Assignments to dotted names or subscripts may fail, in which case the
> assignment may only partially succeed:
>
>
>     spam = 'something'
>     eggs = None
>     spam, eggs.attr = {'spam': 1, 'eggs.attr': 2}
>     # raises AttributeError: 'NoneType' object has no attribute 'attr'
>     # but `spam` may have already been bound to 1
>
>
> (I think that this is undesirable but unavoidable.)
>
>
> Motivating use-cases
> --------------------
>
> The motivation comes from the discussion for scanf-like functionality.
> The addition of dict unpacking assignment would allow something like
> this:
>
>
>     pattern = "I'll have {main} and {extra} with {colour} coffee."
>     string = "I'll have spam and eggs with black coffee."
>     main, extra, colour = **scanf(pattern, string)
>
>     assert main == 'spam'
>     assert extra == 'eggs'
>     assert colour == 'black'
>
>
> But the possibilities are not restricted to string scanning. This will
> allow functions that return multiple values to choose between returning
> them by position or by name:
>
>
>     height, width = get_dimensions(window)  # returns a tuple
>     height, width = **get_dimensions(window)  # returns a mapping
>
> Developers can choose whichever model best suits their API.
>
> Another use-case is dealing with kwargs inside functions and methods:
>
>
>     def method(self, **kwargs):
>         spam, eggs, **kw = **kwargs
>         process(spam, eggs)
>         super().method(**kw)
>
>
> --
> Steve
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/FJYAE6P5263G3MQMVQ5IUCXUNAUJYQAT/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*
<http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UJMLBLEL4W3S4IBG3OO5LH2COCNPS36B/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to