Explicitly recast this proposal as a new kind of decorator clause
This commit is contained in:
parent
13b2b1f160
commit
8257981ad1
119
pep-0403.txt
119
pep-0403.txt
|
@ -1,5 +1,5 @@
|
||||||
PEP: 403
|
PEP: 403
|
||||||
Title: Statement local functions and classes
|
Title: General purpose decorator clause
|
||||||
Version: $Revision$
|
Version: $Revision$
|
||||||
Last-Modified: $Date$
|
Last-Modified: $Date$
|
||||||
Author: Nick Coghlan <ncoghlan@gmail.com>
|
Author: Nick Coghlan <ncoghlan@gmail.com>
|
||||||
|
@ -15,17 +15,18 @@ Resolution: TBD
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
|
||||||
This PEP proposes the addition of a new ``in`` statement that accepts a
|
This PEP proposes the addition of a new ``@in`` decorator clause that makes
|
||||||
statement local function or class definition.
|
it possible to override the name binding step of a function or class
|
||||||
|
definition.
|
||||||
|
|
||||||
The statement accepts a single simple statement that can make a forward
|
The new clause accepts a single simple statement that can make a forward
|
||||||
reference to a trailing function or class definition.
|
reference to decorated function or class definition.
|
||||||
|
|
||||||
This new statement is designed to be used whenever a "one-shot" function or
|
This new clause is designed to be used whenever a "one-shot" function or
|
||||||
class is needed, and placing the function or class definition before the
|
class is needed, and placing the function or class definition before the
|
||||||
statement that uses it actually makes the code harder to read. It also
|
statement that uses it actually makes the code harder to read. It also
|
||||||
avoids any name shadowing concerns by making sure the new name is visible
|
avoids any name shadowing concerns by making sure the new name is visible
|
||||||
only to the statement in the ``in`` clause.
|
only to the statement in the ``@in`` clause.
|
||||||
|
|
||||||
This PEP is based heavily on many of the ideas in PEP 3150 (Statement Local
|
This PEP is based heavily on many of the ideas in PEP 3150 (Statement Local
|
||||||
Namespaces) so some elements of the rationale will be familiar to readers of
|
Namespaces) so some elements of the rationale will be familiar to readers of
|
||||||
|
@ -41,7 +42,7 @@ examples of the kind of code it is designed to simplify.
|
||||||
|
|
||||||
As a trivial example, a weakref callback could be defined as follows::
|
As a trivial example, a weakref callback could be defined as follows::
|
||||||
|
|
||||||
in x = weakref.ref(target, report_destruction)
|
@in x = weakref.ref(target, report_destruction)
|
||||||
def report_destruction(obj):
|
def report_destruction(obj):
|
||||||
print("{} is being destroyed".format(obj))
|
print("{} is being destroyed".format(obj))
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ it's irritating to be forced into it for one-off operations.
|
||||||
If the repetition of the name seems especially annoying, then a throwaway
|
If the repetition of the name seems especially annoying, then a throwaway
|
||||||
name like ``f`` can be used instead::
|
name like ``f`` can be used instead::
|
||||||
|
|
||||||
in x = weakref.ref(target, f)
|
@in x = weakref.ref(target, f)
|
||||||
def f(obj):
|
def f(obj):
|
||||||
print("{} is being destroyed".format(obj))
|
print("{} is being destroyed".format(obj))
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ name like ``f`` can be used instead::
|
||||||
Similarly, a sorted operation on a particularly poorly defined type could
|
Similarly, a sorted operation on a particularly poorly defined type could
|
||||||
now be defined as::
|
now be defined as::
|
||||||
|
|
||||||
in sorted_list = sorted(original, key=f)
|
@in sorted_list = sorted(original, key=f)
|
||||||
def f(item):
|
def f(item):
|
||||||
try:
|
try:
|
||||||
return item.calc_sort_order()
|
return item.calc_sort_order()
|
||||||
|
@ -86,7 +87,7 @@ Rather than::
|
||||||
|
|
||||||
And early binding semantics in a list comprehension could be attained via::
|
And early binding semantics in a list comprehension could be attained via::
|
||||||
|
|
||||||
in funcs = [adder(i) for i in range(10)]
|
@in funcs = [adder(i) for i in range(10)]
|
||||||
def adder(i):
|
def adder(i):
|
||||||
return lambda x: x + i
|
return lambda x: x + i
|
||||||
|
|
||||||
|
@ -94,34 +95,50 @@ And early binding semantics in a list comprehension could be attained via::
|
||||||
Proposal
|
Proposal
|
||||||
========
|
========
|
||||||
|
|
||||||
This PEP proposes the addition of a new ``in`` statement that is a variant
|
This PEP proposes the addition of a new ``@in`` clause that is a variant
|
||||||
of the existing class and function definition syntax.
|
of the existing class and function decorator syntax.
|
||||||
|
|
||||||
The new ``in`` clause replaces the decorator lines, and allows forward
|
The new ``@in`` clause precedes the decorator lines, and allows forward
|
||||||
references to the trailing function or class definition.
|
references to the trailing function or class definition.
|
||||||
|
|
||||||
The trailing function or class definition is always named - the name of
|
The trailing function or class definition is always named - the name of
|
||||||
the trailing definition is then used to make the forward reference from the
|
the trailing definition is then used to make the forward reference from the
|
||||||
preceding statement.
|
``@in`` clause.
|
||||||
|
|
||||||
The ``in`` clause is allowed to contain any simple statement (including those
|
The ``@in`` clause is allowed to contain any simple statement (including
|
||||||
that don't make any sense in that context, such as ``pass`` - while such code
|
those that don't make any sense in that context, such as ``pass`` - while
|
||||||
would be legal, there wouldn't be any point in writing it). This permissive
|
such code would be legal, there wouldn't be any point in writing it). This
|
||||||
structure is easier to define and easier to explain, but a more restrictive
|
permissive structure is easier to define and easier to explain, but a more
|
||||||
approach that only permits operations that "make sense" would also be
|
restrictive approach that only permits operations that "make sense" would
|
||||||
possible (see PEP 3150 for a list of possible candidates).
|
also be possible (see PEP 3150 for a list of possible candidates).
|
||||||
|
|
||||||
The ``in`` statement will not create a new scope - all name binding
|
The ``@in`` clause will not create a new scope - all name binding
|
||||||
operations aside from the trailing function or class definition will affect
|
operations aside from the trailing function or class definition will affect
|
||||||
the containing scope.
|
the containing scope.
|
||||||
|
|
||||||
The name used in the trailing function or class definition is only visible
|
The name used in the trailing function or class definition is only visible
|
||||||
from the associated ``in`` clause, and behaves as if it was an ordinary
|
from the associated ``@in`` clause, and behaves as if it was an ordinary
|
||||||
variable defined in that scope. If any nested scopes are created in either
|
variable defined in that scope. If any nested scopes are created in either
|
||||||
the ``in`` clause or the trailing function or class definition, those scopes
|
the ``@in`` clause or the trailing function or class definition, those scopes
|
||||||
will see the trailing function or class definition rather than any other
|
will see the trailing function or class definition rather than any other
|
||||||
bindings for that name in the containing scope.
|
bindings for that name in the containing scope.
|
||||||
|
|
||||||
|
In a very real sense, this proposal is about making it possible to override
|
||||||
|
the implicit "name = <defined function or class>" name binding operation
|
||||||
|
that is part of every function or class definition, specifically in those
|
||||||
|
cases where the local name binding isn't actually needed.
|
||||||
|
|
||||||
|
Under this PEP, an ordinary class or function definition::
|
||||||
|
|
||||||
|
def name():
|
||||||
|
...
|
||||||
|
|
||||||
|
would be equivalent to::
|
||||||
|
|
||||||
|
@in name = name
|
||||||
|
def name():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
Background
|
Background
|
||||||
==========
|
==========
|
||||||
|
@ -147,7 +164,7 @@ the heavy lifting:
|
||||||
* comprehensions, generator expressions, map(), filter()
|
* comprehensions, generator expressions, map(), filter()
|
||||||
* key arguments to sorted(), min(), max()
|
* key arguments to sorted(), min(), max()
|
||||||
* partial function application
|
* partial function application
|
||||||
* provision of callbacks (e.g. for weak references)
|
* provision of callbacks (e.g. for weak references or aysnchronous IO)
|
||||||
* array broadcast operations in NumPy
|
* array broadcast operations in NumPy
|
||||||
|
|
||||||
However, adopting Ruby's block syntax directly won't work for Python, since
|
However, adopting Ruby's block syntax directly won't work for Python, since
|
||||||
|
@ -197,6 +214,11 @@ function or class definition has been executed.
|
||||||
The ``in`` keyword was chosen as an existing keyword that can be used to
|
The ``in`` keyword was chosen as an existing keyword that can be used to
|
||||||
denote the concept of a forward reference.
|
denote the concept of a forward reference.
|
||||||
|
|
||||||
|
The ``@`` prefix was included in order to exploit the fact that Python
|
||||||
|
programmers are already used to decorator syntax as an indication of
|
||||||
|
out of order execution, where the function or class is actually defined
|
||||||
|
*first* and then decorators are applied in reverse order.
|
||||||
|
|
||||||
For functions, the construct is intended to be read as "in <this statement
|
For functions, the construct is intended to be read as "in <this statement
|
||||||
that references NAME> define NAME as a function that does <operation>".
|
that references NAME> define NAME as a function that does <operation>".
|
||||||
|
|
||||||
|
@ -231,9 +253,9 @@ still a major improvement over the historical situation where everything
|
||||||
Syntax Change
|
Syntax Change
|
||||||
=============
|
=============
|
||||||
|
|
||||||
New::
|
Syntactically, only one new grammar rule is needed::
|
||||||
|
|
||||||
in_stmt: 'in' simple_stmt (classdef|funcdef)
|
in_stmt: '@in' simple_stmt decorated
|
||||||
|
|
||||||
Grammar: http://hg.python.org/cpython/file/default/Grammar/Grammar
|
Grammar: http://hg.python.org/cpython/file/default/Grammar/Grammar
|
||||||
|
|
||||||
|
@ -244,10 +266,10 @@ Possible Implementation Strategy
|
||||||
This proposal has at least one titanic advantage over PEP 3150:
|
This proposal has at least one titanic advantage over PEP 3150:
|
||||||
implementation should be relatively straightforward.
|
implementation should be relatively straightforward.
|
||||||
|
|
||||||
The AST for the ``in`` statement will include both the function or class
|
The ``@in`` clause will be included in the AST for the associated function or
|
||||||
definition and the statement that references it, so it should just be a
|
class definition and the statement that references it. When the ``@in``
|
||||||
matter of emitting the two operations out of order and using a hidden
|
clause is present, it will be emitted in place of the local name binding
|
||||||
variable to link up any references.
|
operation normally implied by a function or class definition.
|
||||||
|
|
||||||
The one potentially tricky part is changing the meaning of the references to
|
The one potentially tricky part is changing the meaning of the references to
|
||||||
the statement local function or namespace while within the scope of the
|
the statement local function or namespace while within the scope of the
|
||||||
|
@ -268,7 +290,7 @@ Calculating attributes without polluting the local namespace (from os.py)::
|
||||||
del _createenviron
|
del _createenviron
|
||||||
|
|
||||||
# Becomes:
|
# Becomes:
|
||||||
in environ = _createenviron()
|
@in environ = _createenviron()
|
||||||
def _createenviron():
|
def _createenviron():
|
||||||
... # 27 line function
|
... # 27 line function
|
||||||
|
|
||||||
|
@ -278,25 +300,45 @@ Loop early binding::
|
||||||
funcs = [(lambda x, i=i: x + i) for i in range(10)]
|
funcs = [(lambda x, i=i: x + i) for i in range(10)]
|
||||||
|
|
||||||
# Becomes:
|
# Becomes:
|
||||||
in funcs = [adder(i) for i in range(10)]
|
@in funcs = [adder(i) for i in range(10)]
|
||||||
def adder(i):
|
def adder(i):
|
||||||
return lambda x: x + i
|
return lambda x: x + i
|
||||||
|
|
||||||
# Or even:
|
# Or even:
|
||||||
in funcs = [adder(i) for i in range(10)]
|
@in funcs = [adder(i) for i in range(10)]
|
||||||
def adder(i):
|
def adder(i):
|
||||||
in return incr
|
@in return incr
|
||||||
def incr(x):
|
def incr(x):
|
||||||
return x + i
|
return x + i
|
||||||
|
|
||||||
A trailing class can be used as a statement local namespace::
|
A trailing class can be used as a statement local namespace::
|
||||||
|
|
||||||
# Evaluate subexpressions only once
|
# Evaluate subexpressions only once
|
||||||
in c = math.sqrt(x.a*x.a + x.b*x.b)
|
@in c = math.sqrt(x.a*x.a + x.b*x.b)
|
||||||
class x:
|
class x:
|
||||||
a = calculate_a()
|
a = calculate_a()
|
||||||
b = calculate_b()
|
b = calculate_b()
|
||||||
|
|
||||||
|
A function can be bound directly to a location which isn't a valid
|
||||||
|
identifier::
|
||||||
|
|
||||||
|
@in dispatch[MyClass] = f
|
||||||
|
def f():
|
||||||
|
...
|
||||||
|
|
||||||
|
Constructs that verge on decorator abuse can be eliminated::
|
||||||
|
|
||||||
|
# Current Python
|
||||||
|
@call
|
||||||
|
def f():
|
||||||
|
...
|
||||||
|
|
||||||
|
# Becomes:
|
||||||
|
@in f()
|
||||||
|
def f():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference Implementation
|
Reference Implementation
|
||||||
========================
|
========================
|
||||||
|
@ -335,6 +377,11 @@ cases::
|
||||||
a = calculate_a()
|
a = calculate_a()
|
||||||
b = calculate_b()
|
b = calculate_b()
|
||||||
|
|
||||||
|
Another past alternative omitted the ``@`` prefix. However, without that
|
||||||
|
prefix, the bare ``in`` keyword didn't associate the clause strongly
|
||||||
|
enough with the subsequent function or class definition. Reusing the
|
||||||
|
decorator prefix and explicitly characterising the new construct as a kind
|
||||||
|
of decorator clause should address that problem.
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
Loading…
Reference in New Issue