_______               __                   _______
       |   |   |.---.-..----.|  |--..-----..----. |    |  |.-----..--.--.--..-----.
       |       ||  _  ||  __||    < |  -__||   _| |       ||  -__||  |  |  ||__ --|
       |___|___||___._||____||__|__||_____||__|   |__|____||_____||________||_____|
                                                             on Gopher (inofficial)
 (HTM) Visit Hacker News on the Web
       
       
       COMMENT PAGE FOR:
 (HTM)   A “frozen” dictionary for Python
       
       
        whimsicalism wrote 1 hour 3 min ago:
        yes! no more `MappingProxyType` hacks
       
        rurban wrote 1 hour 32 min ago:
        > so having a safe way to share dictionaries between threads will be a
        boon
        
        Since only the keys are const, the values not, frozendict is not
        thread-safe per se. There needs to be a small lock around the value
        getter and setter.
       
          varelaz wrote 1 hour 11 min ago:
          it's thread safe on operations on the dict but not on the values.
          Same relates to other immutable structures like tuples. Lock will not
          help here cause unsafety comes from operation on value after value is
          obtained.
       
        the__alchemist wrote 2 hours 41 min ago:
        I wish Python could move away from types-as-mutability-determiners. Or
        paper over them; all could have mutable and non-mutable variants, with
        "to_mut()" and "to_immut()" methods. And in the same vein, explicit
        control over whether functions mutate a variable in place, or make a
        local copy. Python is my first lang, and I still am unable to
        consistently reason about these.
       
        perrygeo wrote 2 hours 57 min ago:
        Python discovers immutable data, and gets it wrong. Frozendict is a
        blunt instrument - instead of a mutable free-for-all, it just locks the
        data for the entire lifecycle. Brace for the wave of code littered with
        deep copies, proudly proclaiming how functional it all is.
        
        If you want real immutable data structures, not a cheap imitation,
        check out pyrsistent.
       
          cylemons wrote 2 hours 42 min ago:
          How else would you "modify" immutable data other than by copying it?
       
            thechao wrote 2 hours 33 min ago:
            And, yet, somehow, functional languages have been doing this since
            forever? I jest, I jest! There's an entire field of study focused
            on this! I'm no expert, but imagine you've got a singly-linked
            list, and we're going to allow only push_back() and pull_back() (no
            insert()). Let's go ahead and let multiple owners have at this
            list. If A does "pull_back()" what really happens is A has a local
            "tail" pointer that moves back along the end of the list and has a
            new belief about the end of the list. When A does "push_back()" it
            begins attaching links at its new tail. Since the links just "point
            backwards" without modifying the old links, this doesn't mutate the
            list "as is", it only mutates A's version of the list. So, if B is
            also looking at the list, it could just start doing "push_back()"
            and add its own "tail". The result is a data-structure that's a
            "bunch of shared singly linked lists" in a bush structure that's
            more akin to a tree than a singly linked list.
            
            You can do the same thing with binary trees and other structures.
            It's more fiddly, and there's definitely places where
            single-ownership allows mutability "under the hood" for real
            performance gains, but that's the basic idea.
       
            ndr wrote 2 hours 35 min ago:
            You don't have to copy everything.
            
            Look up persistent data structures [0]
            
            The main trick is to store everything in a tree with a large
            branching factor.
            
            The elements are on your leaves.
            When you want to make an edit you need to create only the nodes
            from the root to the actual leaf. Then you return the new root.
            Everything else you can share.
            
            This is a log operation, normally implemented with branch 32.
            
            It works with vectors as well as dicts.
            Creating a copy is constant, it's immutable can be shared freely
            (especially on GC'd languages).
            
            Insert/Delete are O(log32(n)) instead of O(n) they'd be with a full
            copy.
            
            [0]
            
 (HTM)      [1]: https://en.wikipedia.org/wiki/Persistent_data_structure#Pe...
       
          shadowgovt wrote 2 hours 47 min ago:
          If your dicts are frozen, you shouldn't need to deep-copy. The point
          of immutability is that if you want a new frozendict based on another
          one, you just rebuild the indirection data structure up top and leave
          the values it references alone.
       
            perrygeo wrote 1 hour 1 min ago:
            You're absolutely right: an "indirection data structure" is
            necessary. Freezing the data is the least interesting part - it
            doesn't give you any of the benefits typically associated with
            immutable data structures in functional languages. That's my point
            - Python is shipping a half solution that's being mistaken for a
            proper one.
            
            You think Python developers are going to roll their own HAMT on top
            of frozendicts? Or are they just gonna make copies? Personally, I'd
            just use pyrsistent which seems to get it right.
       
        ok123456 wrote 3 hours 23 min ago:
        Ruby has had frozen dictionaries since version 1.0, and they are
        generally considered a good thing to use where possible.
       
        Strilanc wrote 3 hours 25 min ago:
        This is a type that I would use a lot.
        
        For example, I often write classes that do cacheable analysis that
        results in a dict (e.g. the class stores a list of tiles defined by
        points and users want a point-to-tiles mapping for convenience). It's
        worth caching those transformations, e.g. using
        @functools.cached_property, but this introduces a risk where any caller
        could ruin the returned cached value by editing it. Currently, I take
        the safety hit (cache a dict) instead of the performance hit (make a
        new copy for each caller). Caching a frozendict would be a better
        tradeoff.
       
          clickety_clack wrote 3 hours 0 min ago:
          Maybe you should take a look at pyrsistent. That allows you to make
          frozen “maps”. You can “evolve” them into new versions of the
          objects and it keeps the references to the unchanged keys and values
          under the hood so it’s fast and memory efficient.
       
        mwsherman wrote 3 hours 42 min ago:
        I’ve found C#’s frozen dictionary to be useful: [1] It’s
        optimized for fast reads in exchange for expensive creation.
        
 (HTM)  [1]: https://learn.microsoft.com/en-us/dotnet/api/system.collection...
       
        sevensor wrote 4 hours 22 min ago:
        Concurrency is a good motivation, but this is super useful even in
        straight line code. There’s a huge difference between functions that
        might mutate a dictionary you pass in to them and functions that
        definitely won’t. Using Mapping is great, but it’s a shallow
        guarantee because you can violate it at run time.
       
        xamuel wrote 4 hours 26 min ago:
        This subject always seems to get bogged down in discussions about
        ordered vs. unordered keys, which to me seems totally irrelevant.
        No-one seems to mention the glaring shortcoming which is that, since
        dictionary keys are required to be hashable, Python has the bizarre
        situation where dicts cannot be dict keys, as in...
        
        {{'foo': 'bar'}: 1, {3:4, 5:6}: 7}
        
        ...and there is no reasonable builtin way to get around this!
        
        You may ask: "Why on earth would you ever want a dictionary with
        dictionaries for its keys?"
        
        More generally, sometimes you have an array, and for whatever reason,
        it is convenient to use its members as keys. Sometimes, the array in
        question happens to be an array of dicts. Bang, suddenly it's
        impossible to use said array's elements as keys! I'm not sure what
        infuriates me more: said impossibility, or the python community's
        collective attitude that "that never happens or is needed, therefore no
        frozendict for you"
       
          kzrdude wrote 4 hours 12 min ago:
          Turning a dictionary into a tuple of tuples `((k1, v1), (k2, v2),
          ...)`; isn't that a reasonable way?
          
          If you want to have hash map keys, you need to think about how to
          hash them and how to compare for equality, it's just that. There will
          be complications to that such as floats, which have a tricky notion
          of equality, or in Python mutable collections which don't want to be
          hashable.
       
            xamuel wrote 2 hours 52 min ago:
            That argument would apply to sets too, and yet frozenset is
            builtin.
       
        lou1306 wrote 4 hours 29 min ago:
        Can someone give a strong rationale for a separate built-in class?
        Because "it prevents any unintended modifications" is a bit weak.
        
        If you have fixed keys, a frozen dataclass will do. If you don't, you
        can always start with a normal dict d, then store
        tuple(sorted(d.items())) to have immutability and efficient lookups
        (binary search), then throw away d.
       
        bilsbie wrote 4 hours 54 min ago:
        Wow weird Mandela effect for me. I really remember this being a built
        and actually using it.
       
          aewens wrote 4 hours 33 min ago:
          You may be thinking of the `frozenset()` built in or the third party
          Python module [frozendict]( [1] )?
          
          Personally, I’ve been using a wrapper around
          `collections.namedtuple` as an underlying data structure to create
          frozen dictionaries when I’ve needed something like that for a
          project.
          
 (HTM)    [1]: https://pypi.org/project/frozendict/
       
            guidopallemans wrote 4 hours 12 min ago:
            When you are making str -> Any dictionaries it's quite likely
            you're better off with dataclasses or namedtuples anyway.
       
              TheFlyingFish wrote 3 hours 31 min ago:
              That works if you're dealing with a known set of keys (i.e. what
              most statically-typed languages would call a struct). It falls
              down if you need something where the keys are unknowable until
              runtime, like a lookup table.
              
              I do like dataclasses, though. I find them sneaking into my code
              more and more as time goes on. Having a declared set of
              properties is really useful, and it doesn't hurt either that
              they're syntactically nicer to use.
       
          hiddencost wrote 4 hours 38 min ago:
          Perhaps immutabledict?
          
 (HTM)    [1]: https://pypi.org/project/immutabledict/
       
          pansa2 wrote 4 hours 42 min ago:
          There was a previous PEP (in 2012) with the exact same title: [1]
          Also one in 2019 for a "frozenmap":
          
 (HTM)    [1]: https://peps.python.org/pep-0416/
 (HTM)    [2]: https://peps.python.org/pep-0603/
       
          Qem wrote 4 hours 46 min ago:
          Perhaps you used the frozen dict implementation from the pip
          installable boltons library:
          
 (HTM)    [1]: https://boltons.readthedocs.io/en/latest/dictutils.html#bolt...
       
        zzzeek wrote 5 hours 11 min ago:
        SQLAlchemy has its own frozendict which we've had in use for many
        years, we have it as a pretty well performing cython implementation
        these days, and I use it ubiquitously.      Would be a very welcome
        addition to the stdlib.
        
        This proposal is important enough that I chimed in on the thread with a
        detailed example of how SQLAlchemy uses this pattern:
        
 (HTM)  [1]: https://discuss.python.org/t/pep-814-add-frozendict-built-in-t...
       
        pansa2 wrote 5 hours 14 min ago:
        I wonder whether Raymond Hettinger has an opinion on this PEP. A long
        time ago, he wrote: "freezing dicts is a can of worms and not
        especially useful".
        
 (HTM)  [1]: https://mail.python.org/pipermail/python-dev/2006-February/060...
       
          morshu9001 wrote 2 min ago:
          I agree, same with frozenset. If you really want to use one of those
          as a key, convert to a tuple. There might be niche use cases for all
          this, but it's not something that the language or even the standard
          lib need to support.
       
          ndr wrote 3 hours 0 min ago:
          Immutability it's a joy to work with. Ask anyone who's worked with
          Clojure's dicts.
       
          dkarl wrote 3 hours 46 min ago:
          It's interesting that he concludes that freezing dicts is "not
          especially useful" after addressing only a single motivation: the use
          of a dictionary as a key.
          
          He doesn't address the reason that most of us in 2025 immediately
          think of, which is that it's easier to reason about code if you know
          that certain values can't change after they're created.
          
          What a change in culture over the last 20 years!
       
          zahlman wrote 4 hours 35 min ago:
          > Another PEP 351 world view is that tuples can serve as frozenlists;
          however, 
          that view represents a Liskov violation (tuples don't support the
          same 
          methods).  This idea resurfaces and has be shot down again every few
          months.
          
          ... Well, yes; it doesn't support the methods for mutation. Thinking
          of ImmutableFoo as a subclass of Foo is never going to work. And,
          indeed, `set` and `frozenset` don't have an inheritance relationship.
          
          I normally find Hettinger very insightful so this one is
          disappointing. But nobody's perfect, and we change over time (and so
          do the underlying conditions). I've felt like frozendict was missing
          for a long time, though. And really I think the language would have
          been better with a more formal concept of immutability (e.g. linking
          it more explicitly to hashability; having explicit recognition of
          "cache" attributes, ...), even if it didn't go the
          immutable-by-default route.
       
            kccqzy wrote 3 hours 15 min ago:
            Apple (or perhaps NeXT) has solved this problem already in
            Objective-C. Look at NSArray and NSMutableArray, or NSData and
            NSMutableData. It’s intuitive and Liskov-correct to make the
            mutable version a subclass of the immutable version. And it’s
            clearly wrong to have the subclass relationship the other way
            around.
            
            Given how dynamic Python is, such a subclass relationship need not
            be evident at the C level. You can totally make one class whose
            implementation is independent of another class a subclass of the
            other, using PEP 3119. This gives implementations complete
            flexibility in how to implement the class while retaining the
            ontological subclass relationship.
       
            pansa2 wrote 4 hours 22 min ago:
            > ImmutableFoo as a subclass of Foo is never going to work. And,
            indeed, `set` and `frozenset` don't have an inheritance
            relationship.
            
            Theoretically, could `set` be a subclass of `frozenset` (and `dict`
            of `frozendict`)? Do other languages take that approach?
            
            > linking [immutability] more explicitly to hashability
            
            AFAIK immutability and hashability are equivalent for the
            language's "core" types. Would it be possible to enforce that
            equivalence for user-defined types, given that mutability and the
            implementation of `__hash__` are entirely controlled by the
            programmer?
       
              kccqzy wrote 3 hours 13 min ago:
              Yes you could. Other languages do. See NSMutableSet and NSSet in
              Objective-C.
       
              chriswarbo wrote 4 hours 7 min ago:
              > Theoretically, could `set` be a subclass of `frozenset` (and
              `dict` of `frozendict`)?
              
              At one extreme: sure, anything can be made a subclass of anything
              else, if we wanted to.
              
              At the other extreme: no, since Liskov substitution is an
              impossibly-high bar to reach; especially in a language that's as
              dynamic/loose as Python. For example, consider an expression like
              '"pop" in dir(mySet)'
       
                tremon wrote 3 hours 48 min ago:
                > consider an expression like '"pop" in dir(mySet)'
                
                  class frozenset:
                    pass
                  
                  class set(frozenset):
                    def pop(self, key):
                      pass
                
                I don't see why hasattr(mySet, 'pop') should be a problem here?
       
          jonathaneunice wrote 4 hours 46 min ago:
          That's a great link and recommended reading.
          
          It explains a lot about the design of Python container classes, and
          the boundaries of polymorphism / duck typing with them, and mutation
          between them.
          
          I don't always agree with the choices made in Python's container
          APIs...but I always want to understand them as well as possible.
          
          Also worth noting that understanding changes over time. Remember when
          GvR and the rest of the core developers argued adamantly against
          ordered dictionaries? Haha! Good times! Thank goodness their first
          wave of understanding wasn't their last. Concurrency and parallelism
          in Python was a TINY issue in 2006, but at the forefront of Python
          evolution these days. And immutability has come a long way as a
          design theme, even for languages that fully embrace stateful change.
       
            zahlman wrote 4 hours 34 min ago:
            > Also worth noting that understanding changes over time. Remember
            when GvR and the rest of the core developers argued adamantly
            against ordered dictionaries? Haha! Good times!
            
            The new implementation has saved space, but there are opportunities
            to save more space (specifically after deleting keys) that they've
            now denied themselves by offering the ordering guarantee.
       
              jonathaneunice wrote 4 hours 7 min ago:
              Ordering, like stability in sorting, is an incredibly useful
              property. If it costs a little, then so be it.
              
              This is optimizing for the common case, where memory is generally
              plentiful and dicts grow more than they shrink. Python has so
              many memory inefficiencies that occasional tombstones in the dict
              internal structure is unlikely to be a major effect. If you're
              really concerned, do `d = dict(d)` after aggressive deletion.
       
                LtWorf wrote 2 hours 2 min ago:
                Does your code actually rely on that? I've never once needed
                it.
       
                zahlman wrote 3 hours 50 min ago:
                > Ordering, like stability in sorting, is an incredibly useful
                property.
                
                I can't say I've noticed any good reasons to rely on it. Didn't
                reach for `OrderedDict` often back in the day either. I've had
                more use for actual sorting than for preserving the insertion
                order.
       
                  mcherm wrote 23 min ago:
                  Personally, I find lots of reasons to prefer an orders Dict
                  to an unordered one. Even small effects like "the debugging
                  output will appear in a consistent order making it easier to
                  compare" can be motivation enough in many use cases.
       
          mvanbaak wrote 4 hours 53 min ago:
          This was 19 (almost) 20 years ago.
          As stated in the lwn.net article, a lot of concurrency has been added
          to python, and it might now be time for something like a frozendict.
          
          Things that were not useful in 2006 might be totally useful in 2026
          ;P
          
          Still, like you, I'm curious wether he has anything to say about it.
       
            aewens wrote 4 hours 20 min ago:
            I think Raymond Hettinger is called out specially here because he
            did a well known talk called [Modern Dictionaries]( [1] ) where
            around 32:00 to 35:00 in he makes the quip about how younger
            developers think they need new data structures to handle new
            problems, but eventually just end up recreating / rediscovering
            solutions from the 1960s.
            
            “What has been is what will be, and what has been done is what
            will be done; there is nothing new under the sun.”
            
 (HTM)      [1]: https://youtu.be/p33CVV29OG8
       
              sesm wrote 4 hours 0 min ago:
              Since that time HAMT was invented and successfully used in Scala
              and Clojure, so this talk didn't age well.
       
                Someone wrote 3 hours 21 min ago:
                Wikipedia ( [1] ) links to the paper describing HAMT ( [2] )
                and claims that is from 2000. That talk is from 2016.
                
 (HTM)          [1]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie
 (HTM)          [2]: https://infoscience.epfl.ch/server/api/core/bitstreams...
       
                  ndr wrote 2 hours 55 min ago:
                  HAMT weren't immutable/persistent until Clojure though.
                  
                  If memory serves that happened 2007-2008, so still well
                  before the talk.
       
        sundarurfriend wrote 5 hours 46 min ago:
        Can someone ELI5 the core difference between this and named tuples, for
        someone who is not deep into Python? ChatGPT's answer boiled down to:
        unordered (this) vs ordered (NTs), "arbitrary keys, decided at runtime"
        vs "fixed set of fields decided at definition time" (can't an NT's keys
        also be interpolated from runtime values?), and a different API
        (`.keys()`, `.items()`), etc (I'm just giving this as context btw, no
        idea if there's inaccuracies in these).
        
        So could this also have been approached from the other side, as in
        making unordered NamedTuples with support for the Mapping API? The line
        between dictionaries and named tuples and structs (across various
        languages) has always seemed a bit blurry to me, so I'm trying to get a
        better picture of it all through this.
       
          willvarfar wrote 5 hours 5 min ago:
          The values in tuples cannot change.  The values that keys point to in
          a frozen dict can?
          
          But yeah I'd be in favour of something that looked a lot like a named
          tuple but with mutable values and supporting [name] access too.  And
          of course some nice syntactic sugar rather like dicts and sets have
          with curly brackets today.
       
            pansa2 wrote 4 hours 59 min ago:
            > The values in tuples cannot change. The values that keys point to
            in a frozen dict can?
            
            The entries of a tuple cannot be assigned to, but the values can be
            mutated. The same is true for a `frozendict` (according to the PEP
            they don't support `__setitem__`, but "values can be mutable").
       
              vscode-rest wrote 4 hours 17 min ago:
              Tuple entries must be hashable, which (as far as standard library
              is concerned) means immutable.
       
                pansa2 wrote 4 hours 15 min ago:
                >>> hash([1, 2])
                  TypeError: unhashable type: 'list'
                
                  >>> t = ([1, 2], [3, 4])
                  >>> print(t)
                  ([1, 2], [3, 4])
       
                  vscode-rest wrote 4 hours 3 min ago:
                  Ah. Of course… that’s how the workaround to use tuples as
                  frozen dicts can work in the first place. Slow morning for
                  me!
       
          quietbritishjim wrote 5 hours 10 min ago:
          > arbitrary keys, decided at runtime" vs "fixed set of fields decided
          at definition time" (can't an NT's keys also be interpolated from
          runtime values?)
          
          If you want to create a named tuple with arbitrary field names at
          runtime then you need to create a new named tuple type before you
          create the instance. That is possible, since Python is a very dynamic
          language, but it's not particularly efficient and, more importantly,
          just feels a bit wrong. And are you going to somehow cache the types
          and reuse them if the field names match? It's all a bit of a mess
          compared to just passing the entries to the frozendict type.
       
          _flux wrote 5 hours 19 min ago:
          The key difference is what the GPT outlined: tuples have order for
          the fields and named tuples are not indexed by the name, but instead
          a field accessor is used, i.e. foo.bar vs foo["bar"]. In addition
          namedtuples can be indexed using that order like tuples can (foo[0]),
          which clearly isn't possible with dicts and would be quite confusing
          if dict had integer key.
          
          So I think the differences aren't great, but they are sufficient. A
          frozendict is not going to be indexable by an integer. Python already
          has an abstract type for this, for mostly the use of type checking I
          imagine: [1] Documentation for namedtuple:
          
 (HTM)    [1]: https://docs.python.org/3/glossary.html#term-mapping
 (HTM)    [2]: https://docs.python.org/3/library/collections.html#collectio...
       
          grimgrin wrote 5 hours 41 min ago:
          I think you could have asked this same comment w/o mentioning ChatGPT
          and you wouldn't have been downvoted to oblivion in 3 minutes
          
          I don't see anything wrong with your asking to understand
       
            chistev wrote 5 hours 3 min ago:
            This place hates ChatGPT and AI. Lol.
            
            Edit: Of course, I get down voted as I predicted I would. Lol.
       
              acdha wrote 4 hours 59 min ago:
              This place hates laziness and imprecision. Using ChatGPT for
              editing or inspiration is okay as long as you personally review
              the results for accuracy and completeness, at which point people
              care about it as much as you announcing that you used a spell
              checker.
       
                delaminator wrote 4 hours 55 min ago:
                Pasting chat GPT responses is against the site rules.
                
                always has been even before GPT
                
 (HTM)          [1]: https://news.ycombinator.com/item?id=46206457
       
                  acdha wrote 4 hours 48 min ago:
                  Fair, because they’re not your words. I’ll edit my
                  comment for what I had in mind: that it can be helpful for
                  that like a spell checker - for example, I know non-native
                  English speakers who find them useful for editing but they
                  completely understand the topic intellectually.
       
        drhagen wrote 6 hours 14 min ago:
        Great! Now make `set` have a stable order and we're done here.
       
          cr125rider wrote 5 hours 53 min ago:
          Aren’t sets unsorted by definition? Or do repeated accesses without
          modification yield different results?
       
            adrian_b wrote 4 hours 27 min ago:
            Not related to Python, but one of the possible implementations of a
            set, i.e. of an equivalence class on sequences, is as a sorted
            array (with duplicates eliminated, unless it is a multiset, where
            non-unique elements are allowed in the sorted array), as opposed to
            the unsorted array that can store an arbitrary sequence.
            
            So sets can be viewed as implicitly sorted, which is why the order
            of the elements cannot be used to differentiate two sets.
            
            Being sorted internally to enforce the equivalence between sets
            with elements provided in different orders does not imply anything
            about the existence of an operation that would retrieve elements in
            a desired order or which would return subsets less or greater than
            a threshold. When such operations are desired, an order relation
            must be externally defined on the set.
            
            So a possible definition of sets and multisets is as sorted arrays
            with or without unicity of elements, while sequences are unsorted
            arrays (which may also have the constraint of unique elements).
            However the standard set operations do not provide external access
            to the internal order, which is an order between arbitrary
            identifiers attached to the elements of the set, which have no
            meaning externally.
       
            Retr0id wrote 4 hours 53 min ago:
            A stable order does not imply sorted order. If you convert the same
            set to a list twice you'll probably get the same order both times
            but it isn't guaranteed anywhere, and that order may change between
            python implementations and versions. The order may also change as
            the set grows or shrinks.
       
            ledauphin wrote 4 hours 55 min ago:
            this is likely in reference to the fact that dicts have maintained
            insertion order since Python ~3.6 as property of the language.
            Mathematically there's no defined order to a set, and a dict is
            really just a set in disguise, but it's very convenient for
            determinism to "add" this invariant to the language.
       
              zahlman wrote 4 hours 24 min ago:
              Sets use a different implementation intentionally (i.e. they are
              not "a dict without values") exactly because it's expected that
              they have different use cases (e.g. union/intersection
              operations).
       
              jonathaneunice wrote 4 hours 42 min ago:
              Debugging is a completely different and better animal when
              collections have a predictable ordering. Else, every dict needs
              ordering before printing, studying, or comparing. Needlessly
              onerous, even if philosophically justifiable.
       
            sltkr wrote 4 hours 57 min ago:
            So are dictionary keys, but Python decided to make them insertion
            ordered (after having them be unordered just like set elements for
            decades). There is no fundamental reason sets couldn't have a
            defined order. That's what languages like JavaScript have done too.
       
              cpburns2009 wrote 4 hours 18 min ago:
              Python's decision to make dict keys ordered in the spec was a
              mistake. It may be the best implementation so far, but it
              eliminates potential improvements in the future.
       
                mrweasel wrote 3 hours 11 min ago:
                Agreed. The only reason to make them sorted is because people
                would wrongly assume that they where. You can argue that a
                programming language should not have unexpected behaviors, and
                apparently unsorted dictionary keys where a surprise to many,
                on the other hand I feel like it's a failure of education.
                
                The problem was that assuming that keys would be sorted was
                frequently true, but not guaranteed. An alternative solution
                would have been to randomize them more, but that would probably
                break a lot of old code. Sorting the keys makes no difference
                if you don't expect them to be, but it will now be a greater
                surprise if you switch language.
       
        drhagen wrote 6 hours 17 min ago:
        If this gets wide enough use, they could add an optimization for code
        like this:
        
            n = 1000
            a = {}
            for i in range(n):
            a[i] = str(i)
            a = frozendict(a)  # O(n) operation can be turned to O(1)
        
        It is relatively easy for the JIT to detect the `frozendict`
        constructor, the `dict` input, and the single reference immediately
        overwritten. Not sure if this would ever be worth it.
       
          _flux wrote 5 hours 26 min ago:
          Wouldn't ref-counting CPython already know that a has a single
          reference, allowing this optimization without needing any particular
          smart JIT?
       
            zahlman wrote 4 hours 26 min ago:
            First thought: I would very much expect it to be able to do this
            optimization given the similar things it does for string
            concatenation.
            
            But actually, I suspect it can't do this optimization simply
            because the name `frozendict` could be shadowed.
       
            zbentley wrote 4 hours 32 min ago:
            I think GP was talking about optimizing away the O(N) call on the
            last line. The GC will take care of removing the reference to the
            old (mutable) dict, but constructing a new frozendict from a
            mutable dict would, in the current proposal, be an O(N) shallow
            copy.
            
            There are also potentially other optimizations that could be
            applied (not specific to dict/frozendict) to reduce the memory
            overhead on operations like "a = f(a)" for selected values of "f".
       
        polyrand wrote 6 hours 26 min ago:
        A frozen dictionary would be very welcome. You can already do something
        similar using MappingProxyType [0]
        
          from types import MappingProxyType
          
          d = {}
          
          d["a"] = 1
          d["b"] = 2
          
          print(d)
          
          frozen = MappingProxyType(d)
          
          print(frozen["a"])
          
          # Error:
          frozen["b"] = "new"
        
        [0]:
        
 (HTM)  [1]: https://docs.python.org/3/library/types.html#types.MappingProx...
       
          zahlman wrote 4 hours 32 min ago:
          > You can already do something similar
          
          Only if you deny access to the underlying real dict.
       
            ali_m wrote 4 hours 6 min ago:
            Yes, this only prevents the callee from mutating it, it can't
            provide a strong guarantee that the underlying mapping won't be
            changed upstream (and hence MappingProxyType can't be washable).
       
        code_biologist wrote 6 hours 39 min ago:
        Shoutout to pyrsistent, a personal favorite library for
        frozen/immutable/hashable data structures whenever I need to use lists,
        sets, or dicts as dict keys, or make sets of such items:
        
 (HTM)  [1]: https://github.com/tobgu/pyrsistent
       
        codethief wrote 6 hours 42 min ago:
        Next step: Auto-inferring the correct (most narrow) TypedDict type from
        a frozendict. (Think `const foo = { … } as const` in TypeScript.) I
        miss this TS feature in Python on the daily.
       
          anentropic wrote 5 hours 16 min ago:
          Agreed... but it shouldn't need a frozendict for this
          
          IMHO TypedDict in Python are essentially broken/useless as is
          
          What is needed is TS style structural matching, like a Protocol for
          dicts
       
          mrweasel wrote 5 hours 56 min ago:
          What would that do exactly? Auto-generate a TypedDict class?
          
          Bringing up TypedDict is sort of interesting, because it seems like a
          frozen dictionary could have been implemented by PEP 705, if
          typing.ReadOnly was enforced at runtime, and not just a hint to a
          static type checker.
       
            dmurray wrote 5 hours 44 min ago:
            Auto-generate a TypedDict type while type checking, while doing
            nothing at runtime.
            
            I expect this is not a very big ask and the various typecheckers
            will have versions of this soon after release.
       
              mrweasel wrote 5 hours 30 min ago:
              Okay so basically just "inline":
              
                class MyType(TypedDict):
                  a: str
                  b: int
              
              or infer the type hint in:
              
                my_typed_dict: dict[str, int] = {"a": 5}
              
              It should probably be something like:
              
                auto_typed: dict[typing.Const, typing.Const] = {"a": 5}
              
              where typing.Const defaults to Any for an empty dict.
       
          adzm wrote 6 hours 3 min ago:
          Curious what this means for typescript as well; honestly I've only
          reached for as const rarely.
       
          virtue3 wrote 6 hours 27 min ago:
          I also SUPER LOVE this feature.  Especially when you make a type
          union of the keys for easier indexing.
       
          tweakimp wrote 6 hours 31 min ago:
          Can you give a Python example of a use case for this?
       
       
 (DIR) <- back to front page