[Python-checkins] peps: Explicitly recast this proposal as a new kind of decorator clause

nick.coghlan python-checkins at python.org
Mon Sep 3 13:19:36 CEST 2012


http://hg.python.org/peps/rev/ba7a581514d0
changeset:   4503:ba7a581514d0
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Mon Sep 03 21:19:27 2012 +1000
summary:
  Explicitly recast this proposal as a new kind of decorator clause

files:
  pep-0403.txt |  129 ++++++++++++++++++++++++++------------
  1 files changed, 88 insertions(+), 41 deletions(-)


diff --git a/pep-0403.txt b/pep-0403.txt
--- a/pep-0403.txt
+++ b/pep-0403.txt
@@ -1,5 +1,5 @@
 PEP: 403
-Title: Statement local functions and classes
+Title: General purpose decorator clause
 Version: $Revision$
 Last-Modified: $Date$
 Author: Nick Coghlan <ncoghlan at gmail.com>
@@ -15,17 +15,18 @@
 Abstract
 ========
 
-This PEP proposes the addition of a new ``in`` statement that accepts a
-statement local function or class definition.
+This PEP proposes the addition of a new ``@in`` decorator clause that makes
+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
-reference to a trailing function or class definition.
+The new clause accepts a single simple statement that can make a forward
+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
 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
-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
 Namespaces) so some elements of the rationale will be familiar to readers of
@@ -41,7 +42,7 @@
 
 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):
         print("{} is being destroyed".format(obj))
 
@@ -59,7 +60,7 @@
 If the repetition of the name seems especially annoying, then a throwaway
 name like ``f`` can be used instead::
 
-    in x = weakref.ref(target, f)
+    @in x = weakref.ref(target, f)
     def f(obj):
         print("{} is being destroyed".format(obj))
 
@@ -67,7 +68,7 @@
 Similarly, a sorted operation on a particularly poorly defined type could
 now be defined as::
 
-    in sorted_list = sorted(original, key=f)
+    @in sorted_list = sorted(original, key=f)
     def f(item):
         try:
             return item.calc_sort_order()
@@ -86,7 +87,7 @@
 
 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):
         return lambda x: x + i
 
@@ -94,34 +95,50 @@
 Proposal
 ========
 
-This PEP proposes the addition of a new ``in`` statement that is a variant
-of the existing class and function definition syntax.
+This PEP proposes the addition of a new ``@in`` clause that is a variant
+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.
 
 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
-preceding statement.
+``@in`` clause.
 
-The ``in`` clause is allowed to contain any simple statement (including those
-that don't make any sense in that context, such as ``pass`` - while such code
-would be legal, there wouldn't be any point in writing it). This permissive
-structure is easier to define and easier to explain, but a more restrictive
-approach that only permits operations that "make sense" would also be
-possible (see PEP 3150 for a list of possible candidates).
+The ``@in`` clause is allowed to contain any simple statement (including
+those that don't make any sense in that context, such as ``pass`` - while
+such code would be legal, there wouldn't be any point in writing it). This
+permissive structure is easier to define and easier to explain, but a more
+restrictive approach that only permits operations that "make sense" would
+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
 the containing scope.
 
 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
-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
 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
 ==========
@@ -147,7 +164,7 @@
   * comprehensions, generator expressions, map(), filter()
   * key arguments to sorted(), min(), max()
   * 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
 
 However, adopting Ruby's block syntax directly won't work for Python, since
@@ -197,6 +214,11 @@
 The ``in`` keyword was chosen as an existing keyword that can be used to
 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
 that references NAME> define NAME as a function that does <operation>".
 
@@ -231,9 +253,9 @@
 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
 
@@ -244,10 +266,10 @@
 This proposal has at least one titanic advantage over PEP 3150:
 implementation should be relatively straightforward.
 
-The AST for the ``in`` statement will include both the function or class
-definition and the statement that references it, so it should just be a
-matter of emitting the two operations out of order and using a hidden
-variable to link up any references.
+The ``@in`` clause will be included in the AST for the associated function or
+class definition and the statement that references it. When the ``@in``
+clause is present, it will be emitted in place of the local name binding
+operation normally implied by a function or class definition.
 
 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
@@ -268,7 +290,7 @@
   del _createenviron
 
   # Becomes:
-  in environ = _createenviron()
+  @in environ = _createenviron()
   def _createenviron():
       ... # 27 line function
 
@@ -278,25 +300,45 @@
   funcs = [(lambda x, i=i: x + i) for i in range(10)]
 
   # Becomes:
-  in funcs = [adder(i) for i in range(10)]
+  @in funcs = [adder(i) for i in range(10)]
   def adder(i):
-    return lambda x: x + i
+      return lambda x: x + i
 
   # Or even:
-  in funcs = [adder(i) for i in range(10)]
+  @in funcs = [adder(i) for i in range(10)]
   def adder(i):
-    in return incr
-    def incr(x):
-        return x + i
+      @in return incr
+      def incr(x):
+          return x + i
 
 A trailing class can be used as a statement local namespace::
 
   # 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:
-    a = calculate_a()
-    b = calculate_b()
+      a = calculate_a()
+      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
 ========================
@@ -335,6 +377,11 @@
     a = calculate_a()
     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
 ==========

-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list