A “frozen” dictionary for Python
Posted by jwilk 1 day ago
Comments
Comment by pansa2 1 day ago
https://mail.python.org/pipermail/python-dev/2006-February/0...
Comment by pkulak 18 hours ago
Kotlin _kinda_ does this as well, but if you have a reference to an immutable map in Kotlin, you are still free to mutate the values (and even keys!) as much as you like.
Comment by rcxdude 16 hours ago
Comment by vlovich123 16 hours ago
Comment by pkulak 16 hours ago
Comment by tekne 15 hours ago
If you in fact return e.g. an Rc::new(thing) or Arc::new(thing), that's forever (though of course you can unwrap the last reference!)
Comment by aw1621107 2 hours ago
Might be worth noting that "dropped" in this context doesn't necessarily correspond to the reference going out of scope:
fn get_first(v: &Vec<i32>) -> &i32 {
&v[0]
}
fn main() {
let mut v = vec![0, 1, 2];
let first = get_first(&v);
print!("{}", first});
v.push(3); // Works!
// print!("{}", first); // Doesn't work
}Comment by the__alchemist 17 hours ago
Comment by jonathaneunice 1 day ago
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.
Comment by zahlman 23 hours ago
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.
Comment by jonathaneunice 23 hours ago
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.
Comment by zahlman 23 hours ago
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.
Comment by mcherm 19 hours ago
Comment by xen0 18 hours ago
I don't often care about a specific order, only that I get the same order every time.
Comment by no_wizard 18 hours ago
Granted, I live and work in TypeScript, where I can't `===` two objects but I could see this deterministic behavior making it easier for a language to compare two objects, especially if equality comparison is dependent on a generated hash.
The other is guaranteed iteration order, if you are reliant on the index-contents relationship of an iterable, but we're talking about Dicts which are keyed, but extending this idea to List, I see this usefulness in some scenarios.
Beyond that, I'm not sure it matters, but I also realize I could simply not have enough imagination at the moment to think of other benefits
Comment by xen0 18 hours ago
But maybe it does all just come down to equality comparisons. Just not always within your own code.
Comment by dontlaugh 16 hours ago
Comment by kzrdude 17 hours ago
For me, it creates more reproducible programs and scripts, even simple ones.
Comment by vanviegen 21 hours ago
Comment by zahlman 21 hours ago
Comment by morshu9001 19 hours ago
Comment by tehnub 19 hours ago
Comment by morshu9001 16 hours ago
Comment by sam_bristow 4 hours ago
Comment by BiteCode_dev 19 hours ago
This morning for example, I tested an object serialized through a JSON API. My test data seems to never match the next run.
After a while, I realized one of the objects was using a set of objects, which in the API was turned into a JSON array, but the order of said array would change depending of the initial Python VM state.
3 days ago, I used itertools.group by to group a bunch of things. But itertools.group by only works on iterable that are sorted by the grouping key.
Now granted, none of those recent example are related to dicts, but dict is not a special case. And it's iterated over regularly.
Comment by seanhunter 21 hours ago
I would expect to use a different data structure if I needed an ordered set.
Comment by LtWorf 21 hours ago
Comment by mvanbaak 1 day ago
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.
Comment by aewens 23 hours ago
“What has been is what will be, and what has been done is what will be done; there is nothing new under the sun.”
Comment by sesm 23 hours ago
Comment by Someone 22 hours ago
Comment by zelphirkalt 21 hours ago
Comment by ndr 22 hours ago
Still well before the talk.
Comment by dkarl 23 hours ago
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!
Comment by morshu9001 19 hours ago
Comment by krick 17 hours ago
Comment by morshu9001 15 hours ago
Comment by zahlman 23 hours ago
... 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.
Comment by kccqzy 22 hours ago
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.
Comment by pansa2 23 hours ago
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?
Comment by kccqzy 22 hours ago
Comment by chriswarbo 23 hours ago
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)'
Comment by tremon 23 hours ago
class frozenset:
pass
class set(frozenset):
def pop(self, key):
pass
I don't see why hasattr(mySet, 'pop') should be a problem here?Comment by chriswarbo 21 hours ago
I never said it's a problem (and I never said it's not!). I was specifically addressing two things:
- The "theoretical" nature of the question I quoted (i.e. ignoring other aspects like subjectivity, practicality, convention, etc.)
- The reasoning about "Liskov violation", which was quoted further up this thread.
For context, here's Liskov's definition of their principle (from https://en.wikipedia.org/wiki/Liskov_substitution_principle ):
> Barbara Liskov and Jeannette Wing described the principle succinctly in a 1994 paper as follows:[1]
> > Subtype Requirement: Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.
My expression `"pop" in dir(mySet)` gives an explicit example of how `set` and `frozenset` are not subtypes of each other (regardless of how they're encoded in the language, with "subclasses" or whatever). In this case `ϕ(x)` would be a property like `'"pop" in dir(x)' = 'False'`, which holds for objects x of type frozenset. Yet it does not hold for objects y of type set.
Your example of `hasattr(mySet, 'pop')` gives another property that would be violated.
My point is that avoiding "Liskov violations" is ("theoretically") impossible, especially in Python (which allows programs to introspect/reflect on values, using facilities like 'dir', 'hasattr', etc.).
(FYI I became rather jaded on the Liskov substitution principle after reading https://okmij.org/ftp/Computation/Subtyping )
Comment by kccqzy 17 hours ago
The root of the issue here is that Liskov substitution principle simply references ϕ(x) to be some property satisfied by objects of a class. It does not distinguish between properties that are designed by the author of the class to be satisfied or properties that happen to be satisfied in this particular implementation. But the Hyrum’s Law also states that properties that are accidentally true can become relied upon and as time passes become an intrinsic property. This to me suggests that the crux of the problem is that people don’t communicate sufficiently about invariants and non-invariants of their code.
Comment by tremon 20 hours ago
This says "if hasattr(parent, 'pop') == True then hasattr(child, 'pop') must be True". This is not violated in this case, since hasattr(parent, 'pop') is False. If you want to extend the above definition so that negative proofs concerning the parent should also hold true for the child, then subtyping becomes impossible since all parent and child types must be identical, by definition.
Comment by minitech 16 hours ago
> If you want to extend the above definition so that negative proofs concerning the parent should also hold true for the child, then subtyping becomes impossible since all parent and child types must be identical, by definition.
The distinction isn’t “negative proofs”, but yes, that’s their point. In Python, you have to draw a line as to which observable properties are eligible.
Comment by FreakLegion 7 hours ago
Type the dict as a mapping when you want immutability:
x: Mapping[int, int] = {1: 1}
x[1] = 2 # Unsupported target for indexed assignment ("Mapping[int, int]").
The only problem I've seen with this is: y = {}
y[x] = 0 # Mypy thinks this is fine. Mapping is hashable, after all!
The issue here is less that dict isn't hashable than that Mapping is, though.Comment by zahlman 17 minutes ago
Comment by tmp10423288442 18 hours ago
Comment by immibis 17 hours ago
When you interpret Liskov substitution properly, it's very rare that anything Liskov-substitutes anything, making the entire property meaningless. So just do things based on what works best in the real world and aim for as much Liskov-substitution as is reasonable. Python is duck-typed anyway.
It's a decent guiding principle - Set and ImmutableSet are more substitutable than Set and Map, so Set deriving from ImmutableSet makes more sense than Set deriving from Map. It's just not something you can ever actually achieve.
Comment by morshu9001 19 hours ago
Comment by boothby 18 hours ago
Comment by morshu9001 18 hours ago
Comment by boothby 17 hours ago
str
bytes
int, float
complex
tuple
frozenset
Aside from int and float, you cannot perform comparisons between objects of different types. Moreover, you cannot sort complex numbers at all.I have crossed that bridge, and I'm telling you (again) that a sorted tuple is not a generic solution.
Comment by morshu9001 17 hours ago
Comment by ndr 22 hours ago
Comment by perrygeo 22 hours ago
If you want real immutable data structures, not a cheap imitation, check out pyrsistent.
Comment by cylemons 22 hours ago
Comment by ndr 21 hours ago
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] https://en.wikipedia.org/wiki/Persistent_data_structure#Pers...
Comment by thechao 21 hours ago
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.
Comment by cylemons 17 hours ago
Comment by tylerhou 13 hours ago
Functional data structures essentially create a proxy on every write. This can be inefficient if you make writes in batches, and you only need immutability between batches.
Comment by shadowgovt 22 hours ago
Comment by perrygeo 20 hours ago
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.
Comment by rented_mule 18 hours ago
Comment by BiteCode_dev 19 hours ago
- 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.
Comment by instig007 17 hours ago
Since when is Python about speed?
> Just ran a quick benchmark
Where's the code? Have you observed the bottleneck call?
> Except at 10k+ items, batchup dates on 100K+ items or inserting 100 keys.
> This is rarely the case in practice
Where's the stats on the actual practice?
> You'd better have an incredible ROI to justify that.
The ROI being: fearless API design where 1) multiple instances of high level components are truly independent and could easily parallelize, 2) calling sites know that they keep the original data intact and that callees behave within the immutability constraints, 3) default func inputs and global scope objects are immutable without having to implement another PEP, 4) collections are hashable in general.
Comment by BiteCode_dev 15 hours ago
I won't waste more of your time.
Comment by instig007 13 hours ago
Comment by polyrand 1 day ago
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.MappingPr...Comment by zahlman 23 hours ago
Only if you deny access to the underlying real dict.
Comment by ali_m 23 hours ago
Comment by code_biologist 1 day ago
Comment by sevensor 23 hours ago
Comment by sundarurfriend 1 day ago
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.
Comment by quietbritishjim 1 day ago
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.
Comment by _flux 1 day ago
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: https://docs.python.org/3/glossary.html#term-mapping
Documentation for namedtuple: https://docs.python.org/3/library/collections.html#collectio...
Comment by minitech 16 hours ago
Comment by quietbritishjim 3 hours ago
Comment by willvarfar 1 day ago
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.
Comment by pansa2 1 day ago
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").
Comment by vscode-rest 23 hours ago
Comment by pansa2 23 hours ago
>>> hash([1, 2])
TypeError: unhashable type: 'list'
>>> t = ([1, 2], [3, 4])
>>> print(t)
([1, 2], [3, 4])Comment by vscode-rest 23 hours ago
Comment by grimgrin 1 day ago
I don't see anything wrong with your asking to understand
Comment by chistev 1 day ago
Edit: Of course, I get down voted as I predicted I would. Lol.
Comment by acdha 1 day ago
Comment by delaminator 1 day ago
always has been even before GPT
Comment by acdha 1 day ago
Comment by grimgrin 22 hours ago
comments don't have to be substantial, but they should be adding something that might merit more responses, or could. yours does not. what kind of reply could you even give to "this place [and so, its users] hates [thing]"
I'd ask you to qualify it, but you can't really qualify such a statement
Comment by rurban 20 hours ago
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.
Comment by varelaz 20 hours ago
Comment by bilsbie 1 day ago
Comment by aewens 23 hours ago
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.
Comment by guidopallemans 23 hours ago
Comment by TheFlyingFish 22 hours ago
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.
Comment by pansa2 1 day ago
https://peps.python.org/pep-0416/
Also one in 2019 for a "frozenmap":
Comment by Qem 1 day ago
Comment by hiddencost 23 hours ago
Comment by kleiba 4 hours ago
Comment by Strilanc 22 hours ago
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.
Comment by clickety_clack 22 hours ago
Comment by codethief 1 day ago
Comment by mrweasel 1 day ago
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.
Comment by dmurray 1 day ago
I expect this is not a very big ask and the various typecheckers will have versions of this soon after release.
Comment by codethief 21 hours ago
Comment by mrweasel 1 day ago
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.Comment by codethief 21 hours ago
What I meant was that
foo = {"a": 5}
should be inferred as foo: TypedDict[{ "a": Literal[5] }] = {"a": 5}Comment by mrweasel 16 hours ago
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.
Comment by adzm 1 day ago
Comment by anentropic 1 day ago
IMHO TypedDict in Python are essentially broken/useless as is
What is needed is TS style structural matching, like a Protocol for dicts
Comment by virtue3 1 day ago
Comment by tweakimp 1 day ago
Comment by mwsherman 23 hours ago
It’s optimized for fast reads in exchange for expensive creation.
Comment by drhagen 1 day ago
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.Comment by _flux 1 day ago
Comment by zbentley 23 hours ago
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".
Comment by tracnar 17 hours ago
Comment by zahlman 23 hours ago
But actually, I suspect it can't do this optimization simply because the name `frozendict` could be shadowed.
Comment by vlovich123 16 hours ago
Comment by emil-lp 5 hours ago
Comment by mont_tag 11 hours ago
People hardly ever use this tool. That suggests that there isn't much of a need for a frozendict.
Comment by the__alchemist 21 hours ago
Comment by Maledictus 21 hours ago
Comment by steadyelk 21 hours ago
[0] https://flax.readthedocs.io/en/latest/api_reference/flax.cor...
Comment by bvrmn 17 hours ago
Comment by ok123456 22 hours ago
Comment by zzzeek 1 day ago
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...
Comment by xamuel 23 hours ago
{{'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"
Comment by kzrdude 23 hours ago
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.
Comment by xamuel 22 hours ago
Comment by shadowgovt 22 hours ago
`.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.
Comment by emil-lp 5 hours ago
Another alternative mentioned was `move`, which would create a frozen version in constant time and clear the original dict.
Comment by whimsicalism 20 hours ago
Comment by westurner 18 hours ago
"PEP 351 – The freeze protocol" (2005, rejected) 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.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_contracthttps://en.wikipedia.org/wiki/Invariant_(mathematics)#Invari... [ https://en.wikipedia.org/wiki/Class_invariant ]
> What is the difference between "invariant" and "constant" and "final"?
Comment by DeathArrow 18 hours ago
Not really, C# has ConcurrentDictionary.
Comment by semiinfinitely 18 hours ago
Comment by drhagen 1 day ago
Comment by cr125rider 1 day ago
Comment by ledauphin 1 day ago
Comment by zahlman 23 hours ago
Comment by jonathaneunice 1 day ago
Comment by adrian_b 23 hours ago
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.
Comment by Retr0id 1 day ago
Comment by sltkr 1 day ago
Comment by cpburns2009 23 hours ago
Comment by mrweasel 22 hours ago
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.
Comment by lou1306 23 hours ago
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.
Comment by malkia 21 hours ago
Comment by neonsunset 22 hours ago