[Python-Dev] PEP 359: The "make" Statement

Steven Bethard steven.bethard at gmail.com
Thu Apr 13 17:58:14 CEST 2006


I know 2.5's not out yet, but since I now have a PEP number, I'm going
to go ahead and post this for discussion.  Currently, the target
version is Python 2.6.  You can also see the PEP at:
    http://www.python.org/dev/peps/pep-0359/

Thanks in advance for the feedback!


PEP: 359
Title: The "make" Statement
Version: $Revision: 45366 $
Last-Modified: $Date: 2006-04-13 07:36:24 -0600 (Thu, 13 Apr 2006) $
Author: Steven Bethard <steven.bethard at gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 05-Apr-2006
Python-Version: 2.6
Post-History: 05-Apr-2006, 06-Apr-2006


Abstract
========

This PEP proposes a generalization of the class-declaration syntax,
the ``make`` statement.  The proposed syntax and semantics parallel
the syntax for class definition, and so::

   make <callable> <name> <tuple>:
       <block>

is translated into the assignment::

   <name> = <callable>("<name>", <tuple>, <namespace>)

where ``<namespace>`` is the dict created by executing ``<block>``.
The PEP is based on a suggestion [1]_ from Michele Simionato on the
python-dev list.


Motivation
==========

Class statements provide two nice facilities to Python:

  (1) They are the standard Python means of creating a namespace.  All
      statements within a class body are executed, and the resulting
      local name bindings are passed as a dict to the metaclass.

  (2) They encourage DRY (don't repeat yourself) by allowing the class
      being created to know the name it is being assigned.

Thus in a simple class statement like::

     class C(object):
         x = 1
         def foo(self):
             return 'bar'

the metaclass (``type``) gets called with something like::

    C = type('C', (object,), {'x':1, 'foo':<function foo at ...>})

The class statement is just syntactic sugar for the above assignment
statement, but clearly a very useful sort of syntactic sugar.  It
avoids not only the repetition of ``C``, but also simplifies the
creation of the dict by allowing it to be expressed as a series of
statements.

Historically, type instances (a.k.a. class objects) have been the only
objects blessed with this sort of syntactic support.  But other sorts
of objects could benefit from such support.  For example, property
objects take three function arguments, but because the property type
cannot be passed a namespace, these functions, though relevant only to
the property, must be declared before it and then passed as arguments
to the property call, e.g.::

    class C(object):
        ...
        def get_x(self):
            ...
        def set_x(self):
            ...
        x = property(get_x, set_x, ...)

There have been a few recipes [2]_ trying to work around this
behavior, but with the new make statement (and an appropriate
definition of property), the getter and setter functions can be
defined in the property's namespace like::

    class C(object):
        ...
        make property x:
            def get(self):
                ...
            def set(self):
                ...

The definition of such a property callable could be as simple as::

    def property(name, args, namespace):
        fget = namespace.get('get')
        fset = namespace.get('set')
        fdel = namespace.get('delete')
        doc = namespace.get('__doc__')
        return __builtin__.property(fget, fset, fdel, doc)

Of course, properties are only one of the many possible uses of the
make statement.  The make statement is useful in essentially any
situation where a name is associated with a namespace.  So, for
example, namespaces could be created as simply as::

    make namespace ns:
        """This creates a namespace named ns with a badger attribute
        and a spam function"""

        badger = 42

        def spam():
            ...

And if Python acquires interfaces, given an appropriately defined
``interface`` callable, the make statement can support interface
creation through the syntax::

    make interface C(...):
        ...

This would mean that interface systems like that of Zope would no
longer have to abuse the class syntax to create proper interface
instances.


Specification
=============

Python will translate a make statement::

    make <callable> <name> <tuple>:
        <block>

into the assignment::

    <name> = <callable>("<name>", <tuple>, <namespace>)

where ``<namespace>`` is the dict created by executing ``<block>``.
The ``<tuple>`` expression is optional; if not present, an empty tuple
will be assumed.

A patch is available implementing these semantics [3]_.

The make statement introduces a new keyword, ``make``.  Thus in Python
2.6, the make statement will have to be enabled using ``from
__future__ import make_statement``.


Open Issues
===========

Does the ``make`` keyword break too much code?  Originally, the make
statement used the keyword ``create`` (a suggestion due to Nick
Coghlan).  However, investigations into the standard library [4]_ and
Zope+Plone code [5]_ revealed that ``create`` would break a lot more
code, so ``make`` was adopted as the keyword instead.  However, there
are still a few instances where ``make`` would break code.  Is there a
better keyword for the statement?

**********

Currently, there are not many functions which have the signature
``(name, args, kwargs)``.  That means that something like::

    make dict params:
        x = 1
        y = 2

is currently impossible because the dict constructor has a different
signature.  Does this sort of thing need to be supported?  One
suggestion, by Carl Banks, would be to add a ``__make__`` magic method
that would be called before ``__call__``.  For types, the ``__make__``
method would be identical to ``__call__`` (and thus unnecessary), but
dicts could support the make statement by defining a ``__make__``
method on the dict type that looks something like::

    def __make__(cls, name, args, kwargs):
        return cls(**kwargs)

Of course, rather than adding another magic method, the dict type
could just grow a classmethod something like ``dict.fromblock`` that
could be used like::

    make dict.fromblock params:
        x = 1
        y = 2


Optional Extensions
===================

Remove the make keyword
-------------------------

It might be possible to remove the make keyword so that such
statements would begin with the callable being called, e.g.::

    namespace ns:
        badger = 42
        def spam():
            ...

    interface C(...):
        ...

However, almost all other Python statements begin with a keyword, and
removing the keyword would make it harder to look up this construct in
the documentation.  Additionally, this would add some complexity in
the grammar and so far I (Steven Bethard) have not been able to
implement the feature without the keyword.


Removing __metaclass__ in Python 3000
-------------------------------------

As a side-effect of its generality, the make statement mostly
eliminates the need for the ``__metaclass__`` attribute in class
objects.  Thus in Python 3000, instead of::

   class <name> <bases-tuple>:
       __metaclass__ = <metaclass>
       <block>

metaclasses could be supported by using the metaclass as the callable
in a make statement::

   make <metaclass> <name> <bases-tuple>:
       <block>

Removing the ``__metaclass__`` hook would simplify the BUILD_CLASS
opcode a bit.


Removing class statements in Python 3000
----------------------------------------

In the most extreme application of make statements, the class
statement itself could be deprecated in favor of ``make type``
statements.


References
==========

.. [1] Michele Simionato's original suggestion
   (http://mail.python.org/pipermail/python-dev/2005-October/057435.html)

.. [2] Namespace-based property recipe
   (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442418)

.. [3] Make Statement patch
   (http://ucsu.colorado.edu/~bethard/py/make_statement.patch)

.. [4] Instances of create in the stdlib
   (http://mail.python.org/pipermail/python-list/2006-April/335159.html)

.. [5] Instances of create in Zope+Plone
   (http://mail.python.org/pipermail/python-list/2006-April/335284.html)


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
   coding: utf-8
   End:


More information about the Python-Dev mailing list