Dict Unpacking in Python

(github.com)

99 points | by _ZeD_ 3 days ago

13 comments

  • yde_java 3 minutes ago
    I use the Python package 'sorcery' in all my production services:

    https://github.com/alexmojaki/sorcery

    It has dict unpacking like this:

        a, b = unpack_keys({'a': 1, 'b': 42})
        assert a == 1
        assert b == 42
  • kristjansson 8 hours ago
    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

  • zdimension 12 hours ago
    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 12 hours ago
      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 3 hours ago
      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 12 hours ago
    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 10 hours ago
      I love the union syntax in 3.9+:

        new_dict = old_dict | update_keys_and_values_dict
      • parpfish 9 hours ago
        Don’t forget the in place variant!

          the_dict |= update_keys_and_values_dict
        • masklinn 6 hours ago
          No no, do forget about it: like += for lists, |= mutates “the dict”, which often makes for awkward bugs.

          And like += over list.extend, |= over dict.update is very little gain, and restricts legal locations (augmented assignments are statements, method calls are expressions even if they return "nothing")

  • sco1 11 hours ago
    The author also has an accompanying video: https://youtu.be/eqiM0xRmFJg
  • nine_k 12 hours ago
    In short, it runs a text preprocessor as the source text decoder (like you would decode from Latin-1 or Shift-JIS to Unicode).
    • agumonkey 7 hours ago
      yeah that's the funny part here, would never have thought of this
  • qwertox 5 hours ago
    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 1 hour ago
      > 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 2 hours ago
      None of the last two LHSes will match `dct`, so you'll get a runtime error.
  • agumonkey 7 hours ago
    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.
  • nikisweeting 7 hours ago
    I would donate $500 to the PSF tomorrow if they added this, the lack of it is daily pain
    • IshKebab 4 hours ago
      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.
    • almostgotcaught 7 hours ago
      you can't do this consistently across all cases without compiler assistance (see https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html or https://peps.python.org/pep-0636/#matching-builtin-classes linked below).
      • nikisweeting 7 hours ago
        perfect is enemy of good imo, dict destructuring is so valuable that I'm willing to bend some rules / add some rules to make it possible. can't we just copy whatever JS does?
        • skeledrew 6 hours ago
          If it's that valuable to you personally you can use that project to remove your "daily pain". No need to inflict the pain caused by such a thing being present in official Python. Some of us like for the language to remain highly readable.
        • almostgotcaught 6 hours ago
          > perfect is enemy of good imo

          You can't land a language feature that only sometimes works - that's absolutely horrid UX.

          > can't we just copy whatever JS does?

          I wasn't aware that js does this and I don't know it's implemented. So maybe I should retract my claim about compiler assistance.

    • crabbone 3 hours ago
      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...

  • odyssey7 2 hours ago
    Python needs a better dictionary. Also, Python needs better names for things than dict.
  • andy99 11 hours ago

      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 11 hours ago
      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 6 hours ago
      Or use itemgetter:

        >>> from operator import itemgetter
        >>> dct = {'greeting': 'hello', 'thing': 'world', 'farewell': 'bye'}
        >>> thing, greeting = itemgetter("thing", "greeting")(dct)
        >>> thing
        'world'
        >>> greeting
        'hello'
      • giingyui 3 hours ago
        There are so many things like this one in the standard library that it kinda pisses me off when I discover a new one.
    • Grikbdl 11 hours ago
      Yours relies on ordering, OP's presumably does not.
    • masklinn 6 hours ago
      TFA looks things up by key, and allows pulling a subset of the dict.
  • unit149 7 hours ago
    [dead]