Tuple parameter unpacking is the use of a tuple as a parameter in afunction signature so as to have a sequence argument automaticallyunpacked. An example is:
deffxn(a,(b,c),d):pass
The use of(b,c) in the signature requires that the secondargument to the function be a sequence of length two (e.g.,[42,-13]). When such a sequence is passed it is unpacked andhas its values assigned to the parameters, just as if the statementb,c=[42,-13] had been executed in the parameter.
Unfortunately this feature of Python’s rich function signatureabilities, while handy in some situations, causes more issues thanthey are worth. Thus this PEP proposes their removal from thelanguage in Python 3.0.
Python has very powerful introspection capabilities. These extend tofunction signatures. There are no hidden details as to what afunction’s call signature is. In general it is fairly easy to figureout various details about a function’s signature by viewing thefunction object and various attributes on it (including the function’sfunc_code attribute).
But there is great difficulty when it comes to tuple parameters. Theexistence of a tuple parameter is denoted by its name being made of a. and a number in theco_varnames attribute of the function’scode object. This allows the tuple argument to be bound to a namethat only the bytecode is aware of and cannot be typed in Pythonsource. But this does not specify the format of the tuple: itslength, whether there are nested tuples, etc.
In order to get all of the details about the tuple from the functionone must analyse the bytecode of the function. This is because thefirst bytecode in the function literally translates into the tupleargument being unpacked. Assuming the tuple parameter isnamed.1 and is expected to unpack to variablesspam andmonty (meaning it is the tuple(spam,monty)), the firstbytecode in the function will be for the statementspam,monty=.1. This means that to know all of the details ofthe tuple parameter one must look at the initial bytecode of thefunction to detect tuple unpacking for parameters formatted as\.\d+ and deduce any and all information about the expectedargument. Bytecode analysis is how theinspect.getargspecfunction is able to provide information on tuple parameters. This isnot easy to do and is burdensome on introspection tools as they mustknow how Python bytecode works (an otherwise unneeded burden as allother types of parameters do not require knowledge of Pythonbytecode).
The difficulty of analysing bytecode not withstanding, there isanother issue with the dependency on using Python bytecode.IronPython[3] does not use Python’s bytecode. Because itis based on the .NET framework it instead stores MSIL[4] infunc_code.co_code attribute of the function. This fact preventstheinspect.getargspec function from working when run underIronPython. It is unknown whether other Python implementations areaffected but is reasonable to assume if the implementation is not justa re-implementation of the Python virtual machine.
As mentioned inIntrospection Issues, to handle tuple parametersthe function’s bytecode starts with the bytecode required to unpackthe argument into the proper parameter names. This means that thereis no special support required to implement tuple parameters and thusthere is no loss of abilities if they were to be removed, only apossible convenience (which is addressed inWhy They Should (Supposedly) Stay).
The example function at the beginning of this PEP could easily berewritten as:
deffxn(a,b_c,d):b,c=b_cpass
and in no way lose functionality.
When looking at the various types of parameters that a Python functioncan have, one will notice that tuple parameters tend to be anexception rather than the rule.
ConsiderPEP 3102 (keyword-only arguments) andPEP 3107 (functionannotations). Both PEPs have been accepted andintroduce new functionality within a function’s signature. And yetfor both PEPs the new feature cannot be applied to tuple parameters asa whole.PEP 3102 has no support for tuple parameters at all (whichmakes sense as there is no way to reference a tuple parameter byname).PEP 3107 allows annotations for each item within the tuple(e.g.,(x:int,y:int)), but not the whole tuple (e.g.,(x,y):int).
The existence of tuple parameters also places sequence objectsseparately from mapping objects in a function signature. There is noway to pass in a mapping object (e.g., a dict) as a parameter and haveit unpack in the same fashion as a sequence does into a tupleparameter.
Consider the following function:
deffxn((a,b),(c,d)):pass
If called asfxn(1,(2,3)) one is given the error messageTypeError:unpacknon-sequence. This error message in no waytells you which tuple was not unpacked properly. There is also noindication that this was a result that occurred because of thearguments. Other error messages regarding arguments to functionsexplicitly state its relation to the signature:TypeError:fxn()takesexactly2arguments(0given), etc.
While an informal poll of the handful of Python programmers I knowpersonally and from the PyCon 2007 sprint indicates a huge majority ofpeople 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 isnot heavily used.
Iterating over every line in Python’s code repository in theLib/directory using the regular expression^\s*def\s*\w+\s*\( todetect function and method definitions there were 22,252 matches inthe trunk.
Tacking on.*,\s*\( to finddef statements that contained atuple parameter, only 41 matches were found. This means that fordef statements, only 0.18% of them seem to use a tuple parameter.
In certain instances tuple parameters can be useful. A common exampleis code that expects a two-item tuple that represents a Cartesianpoint. While true it is nice to be able to have the unpacking of thex and y coordinates for you, the argument is that this small amount ofpractical usefulness is heavily outweighed by other issues pertainingto tuple parameters. And as shown inNo Loss Of Abilities If Removed, their use is purely practical andin no way provide a unique ability that cannot be handled in otherways very easily.
It has been argued that tuple parameters provide a way ofself-documentation for parameters that are expected to be of a certainsequence format. Using our Cartesian point example fromPractical Use, seeing(x,y) as a parameter in a function makesit obvious that a tuple of length two is expected as an argument forthat parameter.
But Python provides several other ways to document what parameters arefor. Documentation strings are meant to provide enough informationneeded to explain what arguments are expected. Tuple parameters mighttell you the expected length of a sequence argument, it does not tellyou what that data will be used for. One must also read the docstringto know what other arguments are expected if not all parameters aretuple parameters.
Function annotations (which do not work with tuple parameters) canalso supply documentation. Because annotations can be of any form,what was once a tuple parameter can be a single argument parameterwith an annotation oftuple,tuple(2),Cartesianpoint,(x,y), etc. Annotations provide great flexibility fordocumenting what an argument is expected to be for a parameter,including being a sequence of a certain length.
To transition Python 2.x code to 3.x where tuple parameters areremoved, two steps are suggested. First, the proper warning is to beemitted when Python’s compiler comes across a tuple parameter inPython 2.6. This will be treated like any other syntactic change thatis to occur in Python 3.0 compared to Python 2.6.
Second, the 2to3 refactoring tool[1] will gain a fixer[2] for translating tuple parameters to being a single parameterthat is unpacked as the first statement in the function. The name ofthe new parameter will be changed. The new parameter will then beunpacked into the names originally used in the tuple parameter. Thismeans that the following function:
deffxn((a,(b,c))):pass
will be translated into:
deffxn(a_b_c):(a,(b,c))=a_b_cpass
As tuple parameters are used by lambdas because of the singleexpression limitation, they must also be supported. This is done byhaving the expected sequence argument bound to a single parameter andthen indexing on that parameter:
lambda(x,y):x+y
will be translated into:
lambdax_y:x_y[0]+x_y[1]
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-3113.rst
Last modified:2025-02-01 08:59:27 GMT