From 4301006e14188586428d6175306cce53c494957e Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 3 Mar 2007 00:42:09 +0000 Subject: [PATCH] Add PEP 3113 (Removal of Tuple Parameter Unpacking). --- pep-0000.txt | 4 + pep-3113.txt | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 pep-3113.txt diff --git a/pep-0000.txt b/pep-0000.txt index 1419244cf..e41dfe612 100644 --- a/pep-0000.txt +++ b/pep-0000.txt @@ -114,6 +114,8 @@ Index by Category S 3101 Advanced String Formatting Talin S 3104 Access to Names in Outer Scopes Yee I 3108 Standard Library Reorganization Cannon + S 3113 Removal of Tuple Parameter Unpacking Cannon + Finished PEPs (done, implemented in Subversion) @@ -457,6 +459,8 @@ Numerical Index SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge SA 3112 Bytes literals in Python 3000 Orendorff + S 3113 Removal of Tuple Parameter Unpacking Cannon + Key diff --git a/pep-3113.txt b/pep-3113.txt new file mode 100644 index 000000000..8b7dfffb3 --- /dev/null +++ b/pep-3113.txt @@ -0,0 +1,266 @@ +PEP: 3113 +Title: Removal of Tuple Parameter Unpacking +Version: $Revision$ +Last-Modified: $Date$ +Author: Brett Cannon +Status: Draft +Type: Standards Track +Python-version: 3.0 +Content-Type: text/x-rst +Created: XX-XXX-XXXX + + +Abstract +======== + +Tuple parameter unpacking is the use of a tuple as a parameter in a +function signature so as to have a sequence argument automatically +unpacked. An example is:: + + def fxn(a, (b, c), d): + pass + +The use of ``(b, c)`` in the signature requires that the second +argument to the function be a sequence of length two (e.g., +``[42, -13]``). When such a sequence is passed it is unpacked and +has its values assigned to the parameters, just as if the statement +``b, c = [42, -13]`` had been executed in the parameter. + +Unfortunately this feature of Python's rich function signature +abilities, while handy in some situations, causes more issues than +they are worth. Thus this PEP proposes their removal from the +language in Python 3.0. + + +Why They Should Go +================== + +Introspection Issues +-------------------- + +Python has very powerful introspection capabilities. These extend to +function signatures. There are no hidden details as to what a +function's call signature is. In general it is fairly easy to figure +out various details about a function's signature by viewing the +function object and various attributes on it (including the function's +``func_code`` attribute). + +But there is great difficulty when it comes to tuple parameters. The +existence of a tuple parameter is denoted by it name being made of a +``.`` and a number in the ``co_varnames`` attribute of the function's +code object. This allows the tuple argument to be bound to a name +that only the bytecode is aware of and cannot be typed in Python +source. But this does not specify the format of the tuple: its +length, whether there are nested tuples, etc. + +In order to get all of the details about the tuple from the function +one must analyse the bytecode of the function. This is because the +first bytecode in the function literally translates into the tuple +argument being unpacked. Assuming the tuple parameter is +named ``.1`` and is expected to unpack to variables ``spam`` and +``monty`` (meaning it is the tuple ``(spam, monty)``), the first +bytecode in the function will be for the statement +``spam, monty = .1``. This means that to know all of the details of +the tuple parameter one must look at the initial bytecode of the +function to detect tuple unpacking for parameters formatted as +``\.\d+`` and deduce any and all information about the expected +argument. Bytecode analysis is how the ``inspect.getargspec`` +function is able to provide information on tuple parameters. This is +not easy to do and is burdensome on introspection tools as they must +know how Python bytecode works (an otherwise unneeded burden as all +other types of parameters do not require knowledge of Python +bytecode). + +The difficulty of analysing bytecode not withstanding, there is +another issue with the dependency on using Python bytecode. +IronPython [#ironpython]_ does not use Python's bytecode. Because it +is based on the .NET framework it instead stores MSIL [#MSIL]_ in +``func_code.co_code`` attribute of the function. This fact prevents +the ``inspect.getargspec`` function from working when run under +IronPython. It is unknown whether other Python implementations are +affected but is reasonable to assume if the implementation is not just +a re-implementation of the Python virtual machine. + + +No Loss of Abilities If Removed +------------------------------- + +As mentioned in `Introspection Issues`_, to handle tuple parameters +the function's bytecode starts with the bytecode required to unpack +the argument into the proper parameter names. This means that their +is no special support required to implement tuple parameters and thus +there is no loss of abilities if they were to be removed, only a +possible convenience (which is addressed in +`Why They Should (Supposedly) Stay`_). + +The example function at the beginning of this PEP could easily be +rewritten as:: + + def fxn(a, b_c, d): + b, c = b_c + pass + +and in no way lose functionality. + + +Exception To The Rule +--------------------- + +When looking at the various types of parameters that a Python function +can have, one will notice that tuple parameters tend to be an +exception rather than the rule. + +Consider PEP 3102 (keyword-only arguments) and PEP 3107 (function +annotations) [#pep-3102]_ [#pep-3107]_. Both PEPs have been accepted and +introduce new functionality within a function's signature. And yet +for both PEPs the new feature cannot be applied to tuple parameters. +This leads to tuple parameters not being on the same footing as +regular parameters that do not attempt to unpack their arguments. + +The existence of tuple parameters also places sequence objects +separately from mapping objects in a function signature. There is no +way to pass in a mapping object (e.g., a dict) as a parameter and have +it unpack in the same fashion as a sequence does into a tuple +parameter. + + +Uninformative Error Messages +---------------------------- + +Consider the following function:: + + def fxn((a, b), (c, d)): + pass + +If called as ``fxn(1, (2, 3))`` one is given the error message +``TypeError: unpack non-sequence``. This error message in no way +tells you which tuple was not unpacked properly. There is also no +indication that this was a result that occurred because of the +arguments. Other error messages regarding arguments to functions +explicitly state its relation to the signature: +``TypeError: fxn() takes exactly 2 arguments (0 given)``, etc. + + +Little Usage +------------ + +While an informal poll of the handful of Python programmers I know +personally and from the PyCon 2007 sprint indicates a huge majority of +people do not know of this feature and the rest just do not use it, +some hard numbers is needed to back up the claim that the feature is +not heavily used. + +Iterating over every line in Python's code repository in the ``Lib/`` +directory using the regular expression ``^\s*def\s*\w+\s*\(`` to +detect function and method definitions there were 22,252 matches in +the trunk. + +Tacking on ``.*,\s*\(`` to find ``def`` statements that contained a +tuple parameter, only 41 matches were found. This means that for +``def`` statements, only 0.18% of them seem to use a tuple parameter. + + +Why They Should (Supposedly) Stay +================================= + +Practical Use +------------- + +In certain instances tuple parameters can be useful. A common example +is code that expect a two-item tuple that reperesents a Cartesian +point. While true it is nice to be able to have the unpacking of the +x and y coordinates for you, the argument is that this small amount of +practical usefulness is heavily outweighed by other issues pertaining +to tuple parameters. And as shown in +`No Loss Of Abilities If Removed`_, their use is purely practical and +in no way provide a unique ability that cannot be handled in other +ways very easily. + + +Self-Documentation For Parameters +--------------------------------- + +It has been argued that tuple parameters provide a way of +self-documentation for parameters that are expected to be of a certain +sequence format. Using our Cartesian point example from +`Practical Use`_, seeing ``(x, y)`` as a parameter in a function makes +it obvious that a tuple of length two is expected as an argument for +that parameter. + +But Python provides several other ways to document what parameters are +for. Documentation strings are meant to provide enough information +needed to explain what arguments are expected. Tuple parameters might +tell you the expected length of a sequence argument, it does not tell +you what that data will be used for. One must also read the docstring +to know what other arguments are expected if not all parameters are +tuple parameters. + +Function annotations (which do not work with tuple parameters) can +also supply documentation. Because annotations can be of any form, +what was once a tuple parameter can be a single argument parameter +with an annotation of ``tuple``, ``tuple(2)``, ``Cartesian point``, +``(x, y)``, etc. Annotations provide great flexibility for +documenting what an argument is expected to be for a parameter, +including being a sequence of a certain length. + + +Transition Plan +=============== + +To transition Python 2.x code to 3.x where tuple parameters are +removed, two steps are suggested. First, the proper warning is to be +emitted when Python's compiler comes across a tuple parameter in +Python 2.6. This will be treated like any other syntactic change that +is to occur in Python 3.0 compared to Python 2.6. + +Second, the 2to3 refactoring tool [#2to3]_ will gain a rule for +translating tuple parameters to being a single parameter this is +unpacked as the first statement in the function. The name of the new +parameter will be a mangling of tuple parameter's names by joining +them with underscores. The new parameter will then be unpacked into +the names originally used in the tuple parameter. This means that the +following function:: + + def fxn((a, (b, c))): + pass + +will be translated into:: + + def fxn(a_b_c): + (a, (b, c)) = a_b_c + pass + + +References +========== + +.. [#2to3] 2to3 refactoring tool + (http://svn.python.org/view/sandbox/trunk/2to3/) + +.. [#ironpython] IronPython + (http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython) + +.. [#MSIL] Microsoft Intermediate Language + (http://msdn.microsoft.com/library/en-us/cpguide/html/cpconmicrosoftintermediatelanguagemsil.asp?frame=true) + +.. [#pep-3102] PEP 3102 (Keyword-Only Arguments) + (http://www.python.org/dev/peps/pep-3102/) + +.. [#pep-3107] PEP 3107 (Function Annotations) + (http://www.python.org/dev/peps/pep-3107/) + +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: