Python Extended Call Syntax Enhancement

Greg Ewing, August 1999

This enhancement extends the procedure calling syntax of Python 1.5.2 to allow passing a tuple and/or dictionary of extra arguments without the need to use apply. This is mainly intended for passing arguments on to an inherited method, for example,

class MyClass(AnotherClass):
def __init__(self, fee, fie, fo, *args, **kw):
AnotherClass.__init__(self, *args, **kw)

Download

extcall.tar.gz
(4732 bytes, last updated 29 August 1999)

What it provides

The full extended argument list syntax is:
arguments :== (argument ',')* (argument | '*' test | '**' test | '*' test , '**' test)
The '*' argument, if present, must be a tuple, and its contents are appended to the ordinary arguments before making the call. The '**' argument, if present, must be a dictionary, and any explicit keyword arguments are added to it before making the call. If there are any collisions between the explicit keyword arguments and the contents of the '**' dictionary, the explicit keyword arguments take precedence.

Limitations

There can be at most one '*' and one '**' argument, and they must appear at the end of the argument list. A case could be made for relaxing this restriction, but it would greatly complicate the implementation. For now, if you want to do anything more elaborate you'll just have to use apply.

The '**' must be two consecutive stars with no space between, unlike the corresponding syntax for a formal parameter list, which allows whitespace between them. If this causes anyone great hardship it could be fixed, but I don't consider it worth the bother right now.

How it Works

Three new opcodes have been defined:
CALL_FUNCTION_STAR
CALL_FUNCTION_STARSTAR
CALL_FUNCTION_STAR_STARSTAR
one for each of the possible combinations of * and ** arguments. The * and ** arguments are simply pushed onto the stack after the regular arguments, and the appropriate opcode emitted.

All the new opcodes lead to the existing code for CALL_FUNCTION in ceval.c, which has been modified to test the opcode and do the appropriate things with the extra arguments. The special case which calls eval_code2 directly when the called object is a PyFunctionObject is only used when there is no * or ** argument. When there is, the code for the general case is entered.

If there is a * argument, an argument tuple is created which is big enough for the explicit arguments plus the contents of the * tuple, and all of these are copied into it. If there is a ** argument, instead of creating a new keyword dictionary, the ** dictionary is used as a starting point; any explicit keyword arguments are then added to it. The resulting tuple and dictionary are then passed to PyObject_CallObjectWithKeywords as usual.