[HN Gopher] Dict Unpacking in Python
___________________________________________________________________
Dict Unpacking in Python
Author : _ZeD_
Score : 131 points
Date : 2025-07-08 16:05 UTC (4 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| zdimension wrote:
| 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 wrote:
| 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).
| almostgotcaught wrote:
| > it's working at the source code level rather than the AST
| level.
|
| this (lexing) is the _only_ use of the codec hack - if you
| want to manipulate the AST you do not need this and can just
| to `ast.parse` and then recompile the function.
| zahlman wrote:
| Indeed; the goto hack works that way (and uses a decorator
| to make it easier to invoke the AST-manipulation logic).
| crabbone wrote:
| 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.
| Y_Y wrote:
| You talking about this?
|
| https://jupyter-
| notebook.readthedocs.io/en/stable/examples/N...
| xg15 wrote:
| I'd argue "import from notebooks" is still only helpful in
| the "space bar heating" sense.
|
| I think Notebooks are great for quick, "explorative" sketches
| of code. They are absolutely terrible for organizing
| "production" code.
|
| I know it often happens that something starts in a notebook
| and then sort of morphs into a generic script or full-on
| application. But I think, this is usually the signal you
| should refactor, pull out the "grown" parts from the
| notebooks and organize them into proper Python modules.
|
| If you have parts that are still experimental or explorative,
| consider importing your new modules _into the notebook_
| instead of the other way around.
|
| Source: personal experience
| nine_k wrote:
| 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 wrote:
| yeah that's the funny part here, would never have thought of
| this
| zelphirkalt wrote:
| 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 wrote:
| I love the union syntax in 3.9+: new_dict =
| old_dict | update_keys_and_values_dict
| parpfish wrote:
| Don't forget the in place variant! the_dict
| |= update_keys_and_values_dict
| masklinn wrote:
| 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")
| IgorPartola wrote:
| The |= does exactly what it says on the tin. How could it
| not mutate the left side of the assignment?
| parpfish wrote:
| In typed languages, I'm all about using nice safe
| immutable variables/values.
|
| But in python, everything is mutable so there's only so
| much safety you can wring out of adhering the an
| immutable style. Any other function can hop in and start
| mutating things (even your "private" attributes). Plan
| for mutations occurring everywhere.
| graemep wrote:
| I find the fewer mutations the easier code is to
| understand, at the level of an individual function.
|
| Of course you do not have the safety you would have in a
| language that enforces immutability, but there is still a
| cost (in terms of maintenance and the likelihood of bugs)
| to mutation.
| masklinn wrote:
| > The |= does exactly what it says on the tin. How could
| it not mutate the left side of the assignment?
|
| The normal way? If the LHS is an integer. |= updates the
| binding but does not mutate the object.
|
| Nothing requires that |= mutate the LHS let alone do so
| unconditionally (e.g. it could update the LHS in place as
| an optimisation iff the refcount indicated that was the
| only reference, which would optimise the case where you
| create a local then update it in multiple steps, but
| would avoid unwittingly updating a parameter in-place).
|
| edit: you might not be understanding what dict.__ior__ is
| doing: >>> a = b = {} >>> c = {1:
| 2} >>> b |= c >>> a {1: 2}
|
| That is, `a |= b` does not merely desugar to `a = a | b`,
| dict.__ior__ does a `self.update(other)` internally
| before updating the binding to its existing value. Which
| also leads to this fun bit of trivial (most known from
| list.__iadd__ but "working" just as well here):
| >>> t = ({},) >>> t[0] |= {1: 2} Traceback
| (most recent call last): File "<stdin>", line 1,
| in <module> TypeError: 'tuple' object does not
| support item assignment >>> t ({1: 2},)
| sco1 wrote:
| The author also has an accompanying video:
| https://youtu.be/eqiM0xRmFJg
| andy99 wrote:
| def u(**kwargs): return tuple(kwargs.values())
|
| Am I missing something, is this effectively the same?
|
| *I realize the tuple can be omitted here
| Grikbdl wrote:
| Yours relies on ordering, OP's presumably does not.
| Izkata wrote:
| 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 wrote:
| 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 wrote:
| There are so many things like this one in the standard
| library that it kinda pisses me off when I discover a new
| one.
| notpushkin wrote:
| Ohhh, nice. Also, attrgetter (which also supports dotted
| notation to get nested attrs! Sadly, no dotted notation for
| itemgetter.)
|
| https://docs.python.org/3/library/operator.html#operator.att.
| ..
| masklinn wrote:
| TFA looks things up by key, and allows pulling a subset of the
| dict.
| kristjansson wrote:
| 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 wrote:
| 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.
| thayne wrote:
| It allows you to use bound variables/constants as long as the
| expression includes a dot so you can distinguish it from a
| capture variable.
|
| Scala allows matching against bound variables but requires it
| either start with an uppercase letter or be surrounded in
| backtics in the pattern. I don't know that that would make
| sense for python, but there could potentially be some special
| syntax to spicify you want to compare against an existing
| variable instead of capturing a new variable.
| xg15 wrote:
| Ah, that makes sense. Maybe the "exceptions" (dots,
| uppercase letters, etc) are needed to permit bound
| variables that we usually don't think of as variables at
| all, like class or package identifiers?
| nikisweeting wrote:
| I would donate $500 to the PSF tomorrow if they added this, the
| lack of it is daily pain
| almostgotcaught wrote:
| 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 wrote:
| 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 wrote:
| 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.
| notpushkin wrote:
| > you can use that project
|
| It's not meant for production use. Quite clearly so:
| https://github.com/asottile/dict-unpacking-at-
| home#please-do...
| almostgotcaught wrote:
| > 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.
| nikisweeting wrote:
| It's been one of the headline features keeping me happy
| in JS for 9+ years.
| IshKebab wrote:
| 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.
| IgorPartola wrote:
| Since when can you use data classes for kwargs? There are
| plenty of times when you should use a dict even if you know
| the keys.
| IshKebab wrote:
| You shouldn't be using kwargs! That is also well known to
| be bad practice (or it should be anyway).
|
| https://medium.com/codex/stop-using-kwargs-as-method-
| argumen...
|
| http://ivory.idyll.org/blog/on-kwargs.html
|
| Give me another one.
| almostgotcaught wrote:
| lol i think you didn't read/understand this - the article
| is about **kwargs (which is sometimes sloppy) while the
| person you're responding to is talking about "exploding"
| a dict when calling a function (this does not require
| **kwargs at all).
| ahupp wrote:
| I'd agree with this, unless the kwargs is typed with the
| new-ish PEP-692: https://peps.python.org/pep-0692/
| crabbone wrote:
| 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...
| agumonkey wrote:
| 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 wrote:
| 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]
| qexat wrote:
| None of the last two LHSes will match `dct`, so you'll get a
| runtime error.
| masklinn wrote:
| > 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.
| odyssey7 wrote:
| Python needs a better dictionary. Also, Python needs better names
| for things than _dict_.
| yde_java wrote:
| 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 wrote:
| That seems a bit crazy and like it would lead to unpredictable
| and hard-to-mantain code. (pardon my candor).
| rrishi wrote:
| im curios why you think so ?
| notpushkin wrote:
| So I see asottile has gone from backporting released features [1]
| to backporting unreleased ones!
|
| [1]: https://pypi.org/p/future-fstrings, mentioned in
| https://github.com/asottile/dict-unpacking-at-home#please-do...
| xg15 wrote:
| 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.
| sametmax wrote:
| Anthony is also the maintainer of the deadsnake ppa, if you were
| searching for reasons to love him more.
| mixmastamyk wrote:
| 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?
| ziofill wrote:
| 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 wrote:
| I don't think they are. They are matched by variable names, so
| this: {thing, greeting} = dct
|
| Should have the exact same result.
| frollogaston wrote:
| 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.
___________________________________________________________________
(page generated 2025-07-12 23:01 UTC)