Q: We have *args and **kwargs. Woud ***allargs be useful?

J

Jonathan Fine

The idioms
def f(*args, **kwargs):
# Do something.
and
args = (1, 2, 3)
kwargs = dict(a=4, b=5)
g(*args, **kwargs)
are often useful in Python.

I'm finding myself picking up /all/ the arguments and storing them for
later use (as part of a testing framework). So for me it would be nice
if I could write
def f(***allargs):
args, kwargs = allargs
# Continue as before.

However, if we do this then 'args' in '*args' is misleading. So I'll
use 'sargs' (for sequence arguments) instead.

I can now write, for a suitable class Args
args = Args(1, 2, 3, a=4, b=5)
g(***args) # Same as before.
sargs, kwargs = args
g(*sargs, **kwargs) # Same as before.

Even better, now that Args is a class we can give it a method 'call' so that
args.call(g)
is equivalent to
g(***args)
which removes the need for the *** construct.

This reminds me of functools.partial except, of course, we've fixed all
the arguments and left the passing of the function for later, whereas in
partial we fix the function and some of the arguments.
http://docs.python.org/library/functools.html#functools.partial

My view are that
1. Conceptually ***allargs is useful, but an Args class would be more
useful (not that it need be either-or).

2. If Args were built in , there could be performance benefits.

3. It's clearer to write
def(*seqargs, **kwargs):
than
def(*args, **kwargs):

4. When the Args class is used a lot, one might welcome
def(***args):
# Do something with args.
as a shortcut (and minor speedup) for
def(*seqargs, **kwargs):
args = Args(*seqargs, **kwargs)
# Do something with args.

I look forward to your comments on this.
 
J

Jon Clements

The idioms
     def f(*args, **kwargs):
         # Do something.
and
     args = (1, 2, 3)
     kwargs = dict(a=4, b=5)
     g(*args, **kwargs)
are often useful in Python.

I'm finding myself picking up /all/ the arguments and storing them for
later use (as part of a testing framework).  So for me it would be nice
if I could write
     def f(***allargs):
          args, kwargs = allargs
          # Continue as before.

However, if we do this then 'args' in '*args' is misleading.  So I'll
use 'sargs' (for sequence arguments) instead.

I can now write, for a suitable class Args
     args = Args(1, 2, 3, a=4, b=5)
     g(***args)   # Same as before.
     sargs, kwargs = args
     g(*sargs, **kwargs)  # Same as before.

Even better, now that Args is a class we can give it a method 'call' so that
     args.call(g)
is equivalent to
     g(***args)
which removes the need for the *** construct.

This reminds me of functools.partial except, of course, we've fixed all
the arguments and left the passing of the function for later, whereas in
partial we fix the function and some of the arguments.
     http://docs.python.org/library/functools.html#functools.partial

My view are that
1.  Conceptually ***allargs is useful, but an Args class would be more
useful (not that it need be either-or).

2.  If Args were built in , there could be performance benefits.

3.  It's clearer to write
         def(*seqargs, **kwargs):
than
         def(*args, **kwargs):

4.  When the Args class is used a lot, one might welcome
         def(***args):
             # Do something with args.
as a shortcut (and minor speedup) for
         def(*seqargs, **kwargs):
             args = Args(*seqargs, **kwargs)
             # Do something with args.

I look forward to your comments on this.

I'm not sure this'll catch on, it'll be interesting to see other
comments.
However, I believe you can get the behaviour you desire something
like:

import inspect

class AllArgs(object):
def __init__(self, func):
self._func = func
self._spec = inspect.getargspec(func)
self._nposargs = len(self._spec.args)
def __call__(self, *args, **kwdargs):
self._func.func_globals['Args'] = (args[self._nposargs:],
kwdargs)
return self._func(*args[:self._nposargs])

@AllArgs
def test():
print Args

@AllArgs
def test2(a, b):
print a, b, Args

test(1, 2, 3, 4, 5, a=3, b=5)
test2(1, 2, 3, 4, 5, c=7)

Done quickly, probably buggy, but does provide 'Args', but without
further work
swallows any *'s and **'s and might ignore defaults (hideously
untested)



hth

Jon.
 
J

Jonathan Fine

Jon said:
I'm not sure this'll catch on, it'll be interesting to see other
comments.
However, I believe you can get the behaviour you desire something
like:

import inspect

class AllArgs(object):
def __init__(self, func):
self._func = func
self._spec = inspect.getargspec(func)
self._nposargs = len(self._spec.args)
def __call__(self, *args, **kwdargs):
self._func.func_globals['Args'] = (args[self._nposargs:],
kwdargs)
return self._func(*args[:self._nposargs])

@AllArgs
def test():
print Args

@AllArgs
def test2(a, b):
print a, b, Args

test(1, 2, 3, 4, 5, a=3, b=5)
test2(1, 2, 3, 4, 5, c=7)

Done quickly, probably buggy, but does provide 'Args', but without
further work
swallows any *'s and **'s and might ignore defaults (hideously
untested)

Thank you for your interest, Jon.

According to http://docs.python.org/library/inspect.html we have that
func_globals is the global namespace in which this function was defined.

Hence we have
>>> def f(): pass ....
>>> f.func_globals is globals() True
>>> f.func_globals ['x'] = 3
>>> x 3
>>>

I don't yet understand what your code is intended to do, but I'm fairly
sure you're not wishing to 'monkey-patch' a global namespace of a module.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top