11 ms·
A “frozen” dictionary for Python
- code_biologist 10mo agoShoutout 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: https://github.com/tobgu/pyrsistent https://github.com/tobgu/pyrsistent
- codethief 10mo agoNext 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.
- tweakimp 10mo agoCan you give a Python example of a use case for this?
- virtue3 10mo agoI also SUPER LOVE this feature. Especially when you make a type union of the keys for easier indexing.
- adzm 10mo agoCurious what this means for typescript as well; honestly I've only reached for as const rarely.
- mrweasel 10mo agoWhat 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 10mo agoAuto-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 10mo agoOkay 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.
- codethief 10mo agoNot sure we're talking about the same thing. Inline TypedDicts are already in the process of being formalized, see https://peps.python.org/pep-0764/ https://peps.python.org/pep-0764/ , and have experimental support in e.g. Pyright. What I meant was that foo = {"a": 5} should be inferred as foo: TypedDict[{ "a": Literal[5] }] = {"a": 5}
- mrweasel 10mo agoAh, I didn't know about PEP 795. I don't really know if I like it though. It looks like something someone invented because they don't want to write classes or specifically want to be able to access a data structure like a dict, for whatever reason. It basically provides data types, but also ensures that you have no easy way to reuse them, and it will miss cases where two data structures happen to have the same signature. It's getting a little messy when we have class, namedtuple and TypedDict, which all can do much the same thing.
- codethief 10mo ago> it will miss cases where two data structures happen to have the same signature. Huh. That's exactly the point: TypedDicts are structural types. > we have class, namedtuple and TypedDict, which all can do much the same thing. No, they can't. The former two are nominal types.
- anentropic 10mo agoAgreed... 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
- polyrand 10mo agoA 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]: https://docs.python.org/3/library/types.html#types.MappingProxyType https://docs.python.org/3/library/types.html#types.MappingPr...
- zahlman 10mo ago> You can already do something similar Only if you deny access to the underlying real dict.
- ali_m 10mo agoYes, 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).
- drhagen 10mo agoIf 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 10mo agoWouldn't ref-counting CPython already know that a has a single reference, allowing this optimization without needing any particular smart JIT?
- zbentley 10mo agoI 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".
- zahlman 10mo agoFirst 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.
- tracnar 10mo agoIndeed, the Tcl implementation does this so e.g. `set d [dict] ; dict set d key value` can modify d in place instead of creating a copy (since everything is immutable).
- drhagen 10mo agoGreat! Now make `set` have a stable order and we're done here.
- cr125rider 10mo agoAren’t sets unsorted by definition? Or do repeated accesses without modification yield different results?
- sltkr 10mo agoSo 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 10mo agoPython'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 10mo agoAgreed. 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.
- zahlman 10mo ago"sorted" and "ordered" mean very different things in this context. And the reason we have ordered dict keys now is because it's trivial with the new compact structure (the actual hash table contains indices to an auxiliary array, which can just be appended to with every insertion). It has nothing to do with any randomization of the hashing process.
- sundarurfriend 10mo agoCan 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.
- grimgrin 10mo agoI 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 10mo agoThis place hates ChatGPT and AI. Lol. Edit: Of course, I get down voted as I predicted I would. Lol.
- acdha 10mo agoThis 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 10mo agoPasting chat GPT responses is against the site rules. always has been even before GPT https://news.ycombinator.com/item?id=46206457 https://news.ycombinator.com/item?id=46206457
- deleted 10mo ago[deleted]
- pansa2 10mo agoI 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". https://mail.python.org/pipermail/python-dev/2006-February/060793.html https://mail.python.org/pipermail/python-dev/2006-February/0...
- mvanbaak 10mo agoThis 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 10mo agoI think Raymond Hettinger is called out specially here because he did a well known talk called [Modern Dictionaries](https://youtu.be/p33CVV29OG8 https://youtu.be/p33CVV29OG8) 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.”
- sesm 10mo agoSince that time HAMT was invented and successfully used in Scala and Clojure, so this talk didn't age well.
- Someone 10mo agoWikipedia (https://en.wikipedia.org/wiki/Hash_array_mapped_trie https://en.wikipedia.org/wiki/Hash_array_mapped_trie) links to the paper describing HAMT (https://infoscience.epfl.ch/server/api/core/bitstreams/f66a3023-2cd0-4b26-af6e-91a9a6ae7450/content https://infoscience.epfl.ch/server/api/core/bitstreams/f66a3...) and claims that is from 2000. That talk is from 2016.
- zzzeek 10mo agoSQLAlchemy 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: https://discuss.python.org/t/pep-814-add-frozendict-built-in-type/104854/100 https://discuss.python.org/t/pep-814-add-frozendict-built-in...
- bilsbie 10mo agoWow weird Mandela effect for me. I really remember this being a built and actually using it.
- Qem 10mo agoPerhaps you used the frozen dict implementation from the pip installable boltons library: https://boltons.readthedocs.io/en/latest/dictutils.html#boltons.dictutils.FrozenDict https://boltons.readthedocs.io/en/latest/dictutils.html#bolt...
- pansa2 10mo agoThere was a previous PEP (in 2012) with the exact same title: https://peps.python.org/pep-0416/ https://peps.python.org/pep-0416/ Also one in 2019 for a "frozenmap": https://peps.python.org/pep-0603/ https://peps.python.org/pep-0603/
- hiddencost 10mo agoPerhaps immutabledict? https://pypi.org/project/immutabledict/ https://pypi.org/project/immutabledict/
- aewens 10mo agoYou may be thinking of the `frozenset()` built in or the third party Python module [frozendict](https://pypi.org/project/frozendict/ https://pypi.org/project/frozendict/)? 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.
- guidopallemans 10mo agoWhen you are making str -> Any dictionaries it's quite likely you're better off with dataclasses or namedtuples anyway.
- TheFlyingFish 10mo agoThat 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.
- lou1306 10mo agoCan 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.
- xamuel 10mo agoThis 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 10mo agoTurning 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.
- zahlman 10mo ago> 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 There is nothing at all bizarre or unexpected about this. Mutable objects should not be expected to be valid keys for a hash-based mapping — because the entire point of that data structure is to look things up by a hash value that doesn't change, but mutating an object in general changes what its hash should be. Besides which, looking things up in such a dictionary is awkward. > More generally, sometimes you have an array, and for whatever reason, it is convenient to use its members as keys. We call them lists, unless you're talking about e.g. Numpy arrays with a `dtype` of `object` or something. I can't think of ever being in the situation you describe, but if the point is that your keys are drawn from the list contents, you could just use the list index as a key. Or just store key-value tuples. It would help if you could point at an actual project where you encountered the problem.
- sevensor 10mo agoConcurrency 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.
- quietbritishjim 10mo ago> There’s a huge difference between functions that might mutate a dictionary you pass in to them and functions that definitely won’t. Maybe I misunderstood, but it sounds to me like you're hoping for the following code to work: def will_not_modify_arg(x: frozendict) -> Result: ... foo = {"a": 1, "b": 2} # type of foo is dict r = will_not_modify_arg(foo) But this won't work (as in, type checkers will complain) because dict is not derived from frozendict (or vice-versa). You'd have to create a copy of the dict to pass it to the function. (Aside from presumably not being what you intended, you can already do that with regular dictionaries to guarantee the original won't change.)
- sevensor 10mo agoNo, I’d type the function argument as a Mapping. Frozendict is so that the function will raise an exception if it violates its type signature. Edit: that is, if as the caller you want foo to be immutable, then you make it a frozendict
- deleted 10mo ago[deleted]
- quietbritishjim 10mo agoAh, I see. The last sentence in your previous comment makes more sense now ("Mapping is great, but ... you can violate it at run time"). A type checker would normally catch violations but I can still see a frozendict would be useful.
- mwsherman 10mo agoI’ve found C#’s frozen dictionary to be useful: https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen.frozendictionary-2?view=net-10.0 https://learn.microsoft.com/en-us/dotnet/api/system.collecti... It’s optimized for fast reads in exchange for expensive creation.
- Strilanc 10mo agoThis 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 10mo agoMaybe 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.
- ok123456 10mo agoRuby has had frozen dictionaries since version 1.0, and they are generally considered a good thing to use where possible.
- perrygeo 10mo agoPython 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.
- shadowgovt 10mo agoIf 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 10mo agoYou'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.
- BiteCode_dev 10mo agopyrsistent is super slow, though. Just ran a quick benchmark: - Creation - 8-12x slower - Lookup - 22-27x slower - Contains check - 30-34x slower - Iteration - 5-14x slower - Merge - 32-158x slower Except at 10k+ items, batchup dates on 100K+ items or inserting 100 keys. This is rarely the case in practice, most dictionaries and dict operations are small, if you have a huge dict, you probably should be chunking your load or delegating that to infra. Not to mention pyrsistent's API is incompatible with dicts, so you can't pass it to external code without conversion. You'd better have an incredible ROI to justify that.
- neonsunset 10mo ago[dead]
- shadowgovt 10mo agoRegarding the spooky-action-at-a-distance concerns of a `.freeze()` method on dict: `.freeze()` should probably just return a frozendict instead of in-place mutating the dict, and they should be separate types. Under the hood, you'll have to build the hashtable anyway to make the frozendict; as long as you're doing that work, you may as well build an object to contain the hashtable and just have that object be separate from the dict that birthed it. The values referenced by both the original dict and the frozendict can be the same values; no need to clone them.
- emil-lp 10mo agoThe point is that freeze could work in constant time, whereas the copying takes linear time. Another alternative mentioned was `move`, which would create a frozen version in constant time and clear the original dict.
- shadowgovt 10mo agoFreeze can't work in constant time if it builds a hash when the dict is frozen so that the dict can be used as a key. If all it does is set a flag that prevents modifications, that's different.
- the__alchemist 10mo agoI 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.
- Maledictus 10mo agoI love Ruby's .freeze
- steadyelk 10mo agoAgree it's about time to make this built in. Other functional packages like JAX [0] are already using the concept but they build it into their library from scratch. [0] https://flax.readthedocs.io/en/latest/api_reference/flax.core.frozen_dict.html https://flax.readthedocs.io/en/latest/api_reference/flax.cor...
- malkia 10mo agoWelcome to starlark :)
- rurban 10mo 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 10mo agoit'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.
- whimsicalism 10mo agoyes! no more `MappingProxyType` hacks
- semiinfinitely 10mo agoits called dataclass
- DeathArrow 10mo ago>But dictionaries are mutable, which makes them problematic for sharing data in concurrent code. Not really, C# has ConcurrentDictionary.
- westurner 10mo agoPMap and PVector, functional Python libraries, "PEP 351 – The freeze protocol" (2005, rejected) https://peps.python.org/pep-0351/ https://peps.python.org/pep-0351/ ; IIUC the freeze protocol proposed basically: def freeze(obj) return cache.setsefault(hash(obj), obj.__freeze__()) /? "Existing threads re: consts and applications thereof" I wasn't able to find a URL to this post (2021) from the python-ideas mailing list archives using a double quoted search term today; I had to use the python mailing list search engine? Did something break crawling of mailing lists? Old mailman HTML archives were very simple to crawl.. ENH: pypa: add a sitemap.xml for/of mailing list archives, forums; @pypa: ask for search engine indexing advice: "How do we make sure that the python mailing list archives will be search indexed?" (as they traditionally were) How to find the .txt of mailing list archives posts these days? From "[Python-ideas] Re: Introduce constant variables in Python" (2021) https://mail.python.org/archives/list/python-ideas@python.org/message/OUAMWO5BOOJ5A727Y7NYPC4OWWJ7OKYP/ https://mail.python.org/archives/list/python-ideas@python.or... : - pyrsistent: PMap, PVector I'm out of time for this; (reformatting this for HN so that URLs will be auto-linkified but newlines won't be eliminated) here's the full email as .txt, the mailing list archive has a hyperlinkified version with newlines preserved. GH Markdown and CommonMark Markdown also preserve newlines and auto-linkify: From: [@westurner] Date: Thu, Jun 17, 2021, 10:43 AM Subject: Re: [Python-ideas] Re: Introduce constant variables in Python Cc: python-ideas <python-ideas@python.org> On Mon, May 24, 2021 at 5:43 PM Chris Angelico <rosuav@gmail.com> wrote: Requiring that a name not be rebound is well-defined and testable. Requiring that an object not change is either trivial (in the case of, say, an integer) or virtually impossible (in the case of most objects). What would be the advantage of such a declaration? ChrisA ## Existing threads re: consts and applications thereof So, `/? from:me pyrsistent` I found a few results: - "[Python-Dev] Challenge: Please break this! (a.k.a restricted mode revisited)" 2016-04 https://mail.python.org/pipermail/python-dev/2016-April/143958.html - ~Sandboxing python within python is nontrivial to impossible; consts might help a bit - https://mail.python.org/pipermail/python-dev/2016-April/143958.html - "Proposal to add const-ness type hints to PEP-484" https://mail.python.org/archives/list/python-ideas@python.org/thread/OVPF5I6IOVF6GOJQRH5UGCCU3R7PQHUF/ - https://github.com/python/typing/issues/242 - "Final names and attributes" https://github.com/python/mypy/pull/5522 This is where `typing.Final` comes from. - "[Python-ideas] "Immutable Builder" Pattern and Operator" https://mail.python.org/pipermail/python-ideas/2017-January/044374.html - [pyrsistent] and "fn.py [do] immutables: https://github.com/kachayev/fn.py/blob/master/README.rst#persistent-data-structures " - "[Python-ideas] Add recordlcass to collections module" https://groups.google.com/g/python-ideas/c/9crHfcCBgYs/m/6_EEaWJAAgAJ - ORMs (e.g. Django, SQLAlchemy) require "dirty state" checking to know which object attributes have changed and need an SQL statement to be executed to synchronize the state; this is relevant because when we're asking for mutable namedtuple we're often trying to do exactly this pattern. - "[Python-ideas] Suggestions: dict.flow_update and dict.__add__" https://www.google.com/search?q=%22%5BPython-ideas%5D+Suggestions%3A+dict.flow_update+and+dict.__add__%22 > dicttoolz has functions for working with these objects; including dicttoolz.merge (which returns a reference to the merged dicts but does not mutate the arguments passed). > > https://toolz.readthedocs.io/en/latest/api.html#dicttoolz > https://toolz.readthedocs.io/en/latest/api.html#toolz.dicttoolz.merge > > pyrsistent has a PRecord class with invariants and type checking that precedes dataclasses. pyrsistent also has 'freeze' and 'thaw' functions for immutability. PRecord extends PMap, which implements __add__ as self.update(arg) (which does not mutate self) https://github.com/tobgu/pyrsistent/blob/master/README.rst#precord > > https://github.com/tobgu/pyrsistent/blob/master/pyrsistent/_pmap.py - "[Python-ideas] How to prevent shared memory from being corrupted ?" https://www.google.com/search?q=%22How+to+prevent+shared+memory+from+being+corrupted+%3F%22 > PyArrow Plasma object ids, "sealing" makes an object immutable, pyristent > > https://arrow.apache.org/docs/python/plasma.html#object-ids > https://arrow.apache.org/docs/python/plasma.html#creating-an-object-buffer > > Objects are created in Plasma in two stages. First, they are created, which allocates a buffer for the object. At this point, the client can write to the buffer and construct the object within the allocated buffer. [...] - [Python-ideas] Experimenting with dict performance, and an immutable dict https://mail.python.org/archives/list/python-ideas@python.org/message/DNBGUJHDH4UTPSETMFFWMJHNXQXIWX4I/ > https://pyrsistent.readthedocs.io/en/latest/intro.html#pyrsistent : > >> Pyrsistent is a number of persistent collections (by some referred to as functional data structures). Persistent in the sense that they are immutable. >> >> All methods on a data structure that would normally mutate it instead return a new copy of the structure containing the requested updates. The original structure is left untouched. >> >> This will simplify the reasoning about what a program does since no hidden side effects ever can take place to these data structures. You can rest assured that the object you hold a reference to will remain the same throughout its lifetime and need not worry that somewhere five stack levels below you in the darkest corner of your application someone has decided to remove that element that you expected to be there. >> >> Pyrsistent is influenced by persistent data structures such as those found in the standard library of Clojure. The data structures are designed to share common elements through path copying. It aims at taking these concepts and make them as pythonic as possible so that they can be easily integrated into any python program without hassle. > What would be the advantage of such a declaration? Constants don't need to be locked or unlocked; which is advantageous for parallelism and reasoning about program correctness. True consts (wherein everything referred to in that object is 'frozen' and immutable or at least only modifiable with e.g. copy-on-write) wouldn't require locks, which would be post-GIL advantageous. You could do consts by never releasing a threading.Lock (or similar): - https://docs.python.org/3/library/asyncio-sync.html#locks - https://docs.python.org/3/library/threading.html#lock-objects - This from https://docs.python.org/2/library/sets.html?highlight=immutable#immutable-transforms re ImmutableSet/FrozenSet is not present in the python 3 docs: https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset Though - even if Python enforced normal consts in the language - all of the other code objects would still be mutable, so you still have the impossibility of sandboxing python. Functional and contracts coding styles rely upon invariance; which can be accomplished with various third-party packages that enforce const-ness throughout what may be an object tree behind that reference that would otherwise need to be copy.deepcopy()'d. ## pyrsistent Src: https://github.com/tobgu/pyrsistent > - PVector, similar to a python list > - PMap, similar to dict > - PSet, similar to set > - PRecord, a PMap on steroids with fixed fields, optional type and invariant checking and much more > - PClass, a Python class fixed fields, optional type and invariant checking and much more > - Checked collections, PVector, PMap and PSet with optional type and invariance checks and more > - PBag, similar to collections.Counter > - PList, a classic singly linked list > - PDeque, similar to collections.deque > - Immutable object type (immutable) built on the named tuple > - freeze and thaw functions to convert between python standard collections and pyrsistent collections. > - Flexible transformations of arbitrarily complex structures built from PMaps and PVectors. ## icontract Src: https://github.com/Parquery/icontract > icontract provides design-by-contract to Python3 with informative violation messages and inheritance. > > It also gives a base for a flourishing of a wider ecosystem: > > - A linter pyicontract-lint, > - A sphinx plug-in sphinx-icontract, > - A tool icontract-hypothesis for automated testing and ghostwriting test files which infers Hypothesis strategies based on the contracts, together with IDE integrations such as icontract-hypothesis-vim, icontract-hypothesis-pycharm, and icontract-hypothesis-vscode, > - Directly integrated into CrossHair, a tool for automatic verification of Python programs, together with IDE integrations such as crosshair-pycharm and crosshair-vscode, and > - An integration with FastAPI through fastapi-icontract to enforce contracts on your HTTP API and display them in OpenAPI 3 schema and Swagger UI. https://en.wikipedia.org/wiki/Design_by_contract https://en.wikipedia.org/wiki/Design_by_contract https://en.wikipedia.org/wiki/Invariant_(mathematics)#Invariants_in_computer_science https://en.wikipedia.org/wiki/Invariant_(mathematics)#Invari... [ https://en.wikipedia.org/wiki/Class_invariant https://en.wikipedia.org/wiki/Class_invariant ] > What is the difference between "invariant" and "constant" and "final"?
- bvrmn 10mo agoIt would be great to have **kwargs as frozendict by default. It could help with caching decorators to speedup key construction. For example natural key is simply `(args, kwargs)`.
- vlovich123 10mo agoI’m curious why the dict->frozendict operation is not an O(1) operation when there’s a refcnt of 1 on the dict - that resolves the spooky action at a distance problem raised and solves the performance for the most common usage pattern (build up the dict progressively and convert to a frozendict for concurrent reads).
- emil-lp 10mo agoBecause optimizations are not added to the PEP, but added later.
- mont_tag 10mo agoNote that Python already offers MappingProxy that wraps a dictionary to hide mutating methods. People hardly ever use this tool. That suggests that there isn't much of a need for a frozendict.
- kleiba 10mo agoOne nice side effect of a dict that's immutable is that under the hood, you could employ perfect hashing so that the your dict uses a hash function that is collision-free.