Python Enhancement Proposals

PEP 351 – The freeze protocol

PEP
351
Title
The freeze protocol
Author
Barry Warsaw <barry at python.org>
Status
Rejected
Type
Standards Track
Created
14-Apr-2005
Post-History


Contents

Abstract

This PEP describes a simple protocol for requesting a frozen, immutable copy of a mutable object. It also defines a new built-in function which uses this protocol to provide an immutable copy on any cooperating object.

Rejection Notice

This PEP was rejected. For a rationale, see this thread on python-dev.

Rationale

Built-in objects such dictionaries and sets accept only immutable objects as keys. This means that mutable objects like lists cannot be used as keys to a dictionary. However, a Python programmer can convert a list to a tuple; the two objects are similar, but the latter is immutable, and can be used as a dictionary key.

It is conceivable that third party objects also have similar mutable and immutable counterparts, and it would be useful to have a standard protocol for conversion of such objects.

sets.Set objects expose a “protocol for automatic conversion to immutable” so that you can create sets.Sets of sets.Sets. PEP 218 deliberately dropped this feature from built-in sets. This PEP advances that the feature is still useful and proposes a standard mechanism for its support.

Proposal

It is proposed that a new built-in function called freeze() is added.

If freeze() is passed an immutable object, as determined by hash() on that object not raising a TypeError, then the object is returned directly.

If freeze() is passed a mutable object (i.e. hash() of that object raises a TypeError), then freeze() will call that object’s __freeze__() method to get an immutable copy. If the object does not have a __freeze__() method, then a TypeError is raised.

Sample implementations

Here is a Python implementation of the freeze() built-in:

def freeze(obj):
    try:
        hash(obj)
        return obj
    except TypeError:
        freezer = getattr(obj, '__freeze__', None)
        if freezer:
            return freezer()
        raise TypeError('object is not freezable')``

Here are some code samples which show the intended semantics:

class xset(set):
    def __freeze__(self):
        return frozenset(self)

class xlist(list):
    def __freeze__(self):
        return tuple(self)

class imdict(dict):
    def __hash__(self):
        return id(self)

    def _immutable(self, *args, **kws):
        raise TypeError('object is immutable')

    __setitem__ = _immutable
    __delitem__ = _immutable
    clear       = _immutable
    update      = _immutable
    setdefault  = _immutable
    pop         = _immutable
    popitem     = _immutable

class xdict(dict):
    def __freeze__(self):
        return imdict(self)

>>> s = set([1, 2, 3])
>>> {s: 4}
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: set objects are unhashable
>>> t = freeze(s)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/usr/tmp/python-lWCjBK.py", line 9, in freeze
TypeError: object is not freezable
>>> t = xset(s)
>>> u = freeze(t)
>>> {u: 4}
{frozenset([1, 2, 3]): 4}
>>> x = 'hello'
>>> freeze(x) is x
True
>>> d = xdict(a=7, b=8, c=9)
>>> hash(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: dict objects are unhashable
>>> hash(freeze(d))
-1210776116
>>> {d: 4}
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: dict objects are unhashable
>>> {freeze(d): 4}
{{'a': 7, 'c': 9, 'b': 8}: 4}

Reference implementation

Patch 1335812 provides the C implementation of this feature. It adds the freeze() built-in, along with implementations of the __freeze__() method for lists and sets. Dictionaries are not easily freezable in current Python, so an implementation of dict.__freeze__() is not provided yet.

Open issues

  • Should we define a similar protocol for thawing frozen objects?
  • Should dicts and sets automatically freeze their mutable keys?
  • Should we support “temporary freezing” (perhaps with a method called __congeal__()) a la __as_temporarily_immutable__() in sets.Set?
  • For backward compatibility with sets.Set, should we support __as_immutable__()? Or should __freeze__() just be renamed to __as_immutable__()?

Source: https://github.com/python-discord/peps/blob/main/pep-0351.txt

Last modified: 2022-01-21 11:03:51 GMT