[Python-Dev] Add a frozendict builtin type (original) (raw)

Nick Coghlan ncoghlan at gmail.com
Tue Feb 28 02:00:08 CET 2012


On Tue, Feb 28, 2012 at 9:34 AM, Victor Stinner <victor.stinner at gmail.com> wrote:

The blacklist implementation has a major issue: it is still possible to call write methods of the dict class (e.g. dict.set(myfrozendict, key, value)).

It is also possible to use ctypes and violate even more invariants. For most purposes, this falls under "consenting adults". My primary usage of frozendict would be pysandbox, a security module. Attackers are not consenting adults :-) Read-only dict would also help optimization, in the CPython peephole or the PyPy JIT.

I'm pretty sure the PyPy jit can already pick up and optimise cases where a dict goes "read-only" (i.e. stops being modified).

I think you need to elaborate on your use cases further, and explain what additional changes would be needed, such as allowing frozendict instances as dict attributes in order to create truly immutable objects in pure Python code.

In fact, that may be a better way to pitch the entire PEP. In current Python, you can't create a truly immutable object without dropping down to a C extension:

from decimal import Decimal x = Decimal(1) x Decimal('1') hash(x) 1 x.exp = 10 x Decimal('1E+10') hash(x) 10000000000

Contrast that with the behaviour of a float instance:

1.0.imag = 1 Traceback (most recent call last): File "", line 1, in AttributeError: attribute 'imag' of 'float' objects is not writable

Yes, it's arguably covered by the "consenting adults" rule, but really, Decimal instances should be just as immutable as int and float instances. The only reason they aren't is that it's hard enough to set it up in Python code that the Decimal implementation settles for "near enough is good enough" and just uses slots to prevent addition of new attributes, but doesn't introduce the overhead of custom setattr and delattr implementations to actively prevent modifications.

We don't even need a new container type, we really just need an easy way to tell the setattr and delattr descriptors for "slots" that the instance initialisation is complete and further modifications should be disallowed.

For example, if Decimal.new could call "self.lock_slots()" at the end to set a flag on the instance object, then the slot descriptors could read that new flag and trigger an error:

x.exp = 10 Traceback (most recent call last): File "", line 1, in AttributeError: attribute '_exp' of 'Decimal' objects is not writable

To be clear, all of this is currently possible if you use custom descriptors (such as a property() implementation where setattr and delattr look for such a flag) or override setattr/delattr. However, for a micro-optimised type like Decimal, that's a hard choice to be asked to make (and the current implementation came down on the side of speed over enforcing correctness). Given that using slots in the first place is, in and of itself, a micro-optimisation, I suspect Decimal is far from the only "immutable" type implemented in pure Python that finds itself having to make that trade-off. (An extra boolean check in C is a good trade-off of speed for correctness. Python level descriptor implementations or attribute access overrides, on the other hand... not so much).

Cheers, Nick.

-- Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia



More information about the Python-Dev mailing list