1

This example is taken from Beazley, Python Essential Reference 4e, pg:101.

How is he doing:

func(*args, **kwargs)

where 'func' is the square-function which takes 1 argument. Earlier in the chapter he sqawks about how the position and number of arguments must match in a call/definition or a TypeError would be raised.

Also,

@trace
def square(x):
    ...
square = trace(square)

trace returns 'callf' so this is equivalent to writing: square = callf which is fine because since square refers to a new-function-object, you can call it with *args, **kwargs. But, then in callf he does func(*args...

Given that we just made 'square' refer to some other object, how is the original square accessible inside? What mechanism is coming into play?

@trace
def square(x):
    return x*x

enable_tracing = True
if enable_tracing:
    debug_log = open("debug.log","w")

def trace(func):
    if enable_tracing:
        def callf(*args,**kwargs):
            debug_log.write("Calling %s: %s, %s\n" % 
                (func.__name__, args, kwargs))
            r = func(*args,**kwargs) #????????
            debug_log.write("%s returned %s\n" % (func.__name, r))
            return r
        return callf
    else:
        return func
Jon Clements
  • 138,671
  • 33
  • 247
  • 280

1 Answers1

0

The *-prefix means, "Use this sequence of values as the positional parameters to the function." The **-prefix means, "Use this dictionary as the named parameters to the function." If the sequence is empty, then no positional parameters are passed. If the dictionary is empty, then no named parameters are passed.

When you define a function with those prefixes, then the unaccounted for positional parameters go into the *-prefixed argument, and the unaccounted for named parameters go into the **-prefixed argument. So if you define a function like this:

def wrapper(*args, **kwargs):

then the function can be invoked with any arguments whatsoever. If that function then calls another function with those arguments, then it will be called however the wrapper was called.

Note that you can call a function with (*args, **kwargs) even if wasn't defined that way:

>>> def square(x):
...     return x*x
...
>>> args = (10,)
>>> kwargs = {}
>>> square(*args, **kwargs)
100

Because kwargs is empty, there are no named parameters passed to the function. It gets only the one positional arguments in args.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • right, I got that part BUT within the closure, if you do: fname(*args, **kwargs) won't that pass everything into square() which really expects just one arg (assuming you have a user who misuses the class) he has no check for no of args within the closure.. –  Jan 22 '13 at 12:25