275 lines
7.9 KiB
Plaintext
275 lines
7.9 KiB
Plaintext
PEP: 359
|
||
Title: The "make" Statement
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Steven Bethard <steven.bethard@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:
|