PEP: 351 Title: The freeze protocol Version: 2.5 Last-Modified: $Date$
Author: Barry Warsaw <barry@python.org> Status: Rejected Type: Standards
Track Content-Type: text/x-rst Created: 14-Apr-2005 Post-History:

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`\_.

.. \_this thread on python-dev:
https://mail.python.org/pipermail/python-dev/2006-February/060793.html

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.

.. \_1335812:
http://sourceforge.net/tracker/index.php?func=detail&aid=1335812&group\_id=5470&atid=305470

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**()?

Copyright
=========

This document has been placed in the public domain.

 .. Local Variables: mode: indented-text indent-tabs-mode: nil
sentence-end-double-space: t fill-column: 70 End:
