top | item 44501287

Dict Unpacking in Python

146 points| _ZeD_ | 8 months ago |github.com

65 comments

order
[+] xg15|8 months ago|reply
Lame: Submit a PEP, campaign for community support, write a patch, go back and forth with the maintainers, endure weeks and months of bikeshedding, then maybe, eventually have your feature included in the next Python release.

Game: Use the codec hack, immediately publish your feature for all Python versions, then write "please do not use" to be safe.

[+] kristjansson|8 months ago|reply
While not nearly as fun as the OP, I’d note that this sort of unpacking is very pleasant in the newish PEP636 match case statements:

https://peps.python.org/pep-0636/#matching-builtin-classes

[+] xg15|8 months ago|reply
Looks really cool!

Will this allow combinations of bound and unbound variables?

E.g.:

  def is_on_horizontal_line(point, line_y):
    match point:
      case (x, line_y):
        return f"Yes, with x={x}"
      case _:
        return "No"
Seems both useful and potentially confusing.
[+] zdimension|8 months ago|reply
Did not know that such things could be accomplished by registering a new file coding format. Reminds me of https://pypi.org/project/goto-statement/
[+] zahlman|8 months ago|reply
This one is arguably even more of a hack; it's working at the source code level rather than the AST level.

The "coding" here is a bytes-to-text encoding. The Python lexer expects to see character data; you get to insert arbitrary code to convert the bytes to characters (or just use existing schemes the implement standards like UTF-8).

[+] crabbone|8 months ago|reply
I think there's a package to treat Jupyter notebooks as source code (so you can import them as modules).

While the OP package is obviously a joke, the one with notebooks is kind of useful. And, of course, obligatory quote about how languages that don't have meta-programming at the design level will reinvent it, but poorly.

[+] zelphirkalt|8 months ago|reply
I found dictionary unpacking to be quite useful, when you don't want to mutate things. Code like:

    new_dict = {**old_dict, **update_keys_and_values_dict}
Or even complexer:

    new_dict = {
        **old_dict,
        **{
            key: val
            for key, val in update_keys_and_values_dict
            if key not in some_other_dict
        }
    }
It is quite flexible.
[+] peter422|8 months ago|reply
I love the union syntax in 3.9+:

  new_dict = old_dict | update_keys_and_values_dict
[+] yde_java|8 months ago|reply
I use the Python package 'sorcery' [0] in all my production services.

It gives dict unpacking but also a shorthand dict creation like this:

    from sorcery import dict_of, unpack_keys
    a, b = unpack_keys({'a': 1, 'b': 42})
    assert a == 1
    assert b == 42
    assert dict_of(a, b) == {'a': 1, 'b': 42}
[0] https://github.com/alexmojaki/sorcery
[+] john-radio|8 months ago|reply
That seems a bit crazy and like it would lead to unpredictable and hard-to-mantain code. (pardon my candor).
[+] frollogaston|8 months ago|reply
After using JS, Python dicts and objects feel so cumbersome. I don't see why they need to be separate things, and why you can't access a dict like `dict.key`. Destructuring is the icing on the cake. In JS, it even handles the named args use case like

   const foo = ({name, age, email}) => { }
I'm guessing all of this has been proposed in Python before, and rejected in part because at this point it'd create way more confusion than it's worth.
[+] tkcranny|8 months ago|reply
I don’t mind the distinction of it as a map container keeping dot properties/methods separate from the keyed values. But yeah the endless string quoting is painful coming back from JS, bare key literals in constructors like JS would be a welcome addition for sure, as would named key unpacking like this whole post is about.
[+] nine_k|8 months ago|reply
In short, it runs a text preprocessor as the source text decoder (like you would decode from Latin-1 or Shift-JIS to Unicode).
[+] tkcranny|8 months ago|reply
Yeah I had totally forgotten about this. I remember seeing it around a bit in the python 2 days when UTF-8 wasn’t always assumed. The fact a ~macro system can be bolted on using this is impressive, hilarious, and shockingly terrible.
[+] agumonkey|8 months ago|reply
yeah that's the funny part here, would never have thought of this
[+] agumonkey|8 months ago|reply
Coming from lisp/haskell I always wanted destructuring but after using it quite a lot in ES6/Typescript, I found it's not always as ergonomic and readable as I thought.
[+] qwertox|8 months ago|reply
This confuses me a bit

  dct = {'a': [1, 2, 3]}
  {'a': [1, *rest]} = dct
  print(rest)  # [2, 3]
Does this mean that i can use?

  dct = {'a': [1, 2, 3]}
  {'b': [4, *rest]} = dct
  print(rest)  # [2, 3]
and more explicit

  dct = {'a': [1, 2, 3]}
  {'_': [_, *rest]} = dct
  print(rest)  # [2, 3]
[+] masklinn|8 months ago|reply
> Does this mean that i can use?

They'll both trigger a runtime error, since the key you're using in the pattern (LHS) does not match any key in the dict.

Note that `'_'` is an actual string, and thus key, it's not any sort of wildcard. Using a bare `_` as key yields a syntax error, I assume because it's too ambiguous for the author to want to support it.

[+] qexat|8 months ago|reply
None of the last two LHSes will match `dct`, so you'll get a runtime error.
[+] sametmax|8 months ago|reply
Anthony is also the maintainer of the deadsnake ppa, if you were searching for reasons to love him more.
[+] mixmastamyk|8 months ago|reply
Believe he’s the same person who won’t allow pyflakes to support # noqa, because it’s “opinionated.”

As if dropping that word is some sort of justification. I don’t know what the opinion is! Worse is better?

[+] nikisweeting|8 months ago|reply
I would donate $500 to the PSF tomorrow if they added this, the lack of it is daily pain
[+] IshKebab|8 months ago|reply
You shouldn't be using dicts for data that you know the name of anyway - use dataclasses or named tuples. Dicts are best for things with keys that are not known at compile time.
[+] crabbone|8 months ago|reply
Now come on... for code golf? Why on Earth would anyone want extra syntax in a language with already tons of bloat in the syntax that contribute nothing to language's capabilities? It's, in Bill Gates words, like paying to make airplanes heavier...

This package is a funny gimmick, to illustrate, probably, unintended consequences of some of the aspects of Python's parser. Using this for anything other than another joke is harmful...

[+] ziofill|8 months ago|reply
The confusing bit to me is that the LHS of this

{greeting, thing} = dct

is a set, which is not ordered, so why would greeting and thing be assigned in the order in which they appear?

[+] xg15|8 months ago|reply
I don't think they are. They are matched by variable names, so this:

  {thing, greeting} = dct
Should have the exact same result.
[+] andy99|8 months ago|reply

  def u(**kwargs):
    return tuple(kwargs.values())
Am I missing something, is this effectively the same?

*I realize the tuple can be omitted here

[+] Izkata|8 months ago|reply
You have to pull them out by key name, and not just get everything. Here's a working version, though with a totally different syntax (to avoid having to list the keys twice, once as keys and once as resulting variable names):

  >>> def u(locals, dct, keys):
  ...     for k in keys:
  ...         locals[k] = dct[k]
  ... 
  >>> dct = {'greeting': 'hello', 'thing': 'world', 'farewell': 'bye'}
  >>> u(locals(), dct, ['greeting', 'thing'])
  >>> greeting
  'hello'
  >>> thing
  'world'
  >>> farewell
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  NameError: name 'farewell' is not defined

Modifying locals() is generally frowned upon, as there's no guarantee it'll work. But it does for this example.
[+] sischoel|8 months ago|reply
Or use itemgetter:

  >>> from operator import itemgetter
  >>> dct = {'greeting': 'hello', 'thing': 'world', 'farewell': 'bye'}
  >>> thing, greeting = itemgetter("thing", "greeting")(dct)
  >>> thing
  'world'
  >>> greeting
  'hello'
[+] Grikbdl|8 months ago|reply
Yours relies on ordering, OP's presumably does not.
[+] masklinn|8 months ago|reply
TFA looks things up by key, and allows pulling a subset of the dict.