1

I'd like to be able to apply some function fn to all arguments args arbitrarily i.e. regardless of the structure of the arguments. A recursive approach works.

def convert_args(args, fn):
    if isinstance(args, tuple):
        ret_list = [convert_args(i, fn) for i in args]
        return tuple(ret_list)
    elif isinstance(args, list):
        return [convert_args(i, fn) for i in args]
    elif isinstance(args, dict):
        return {k: convert_args(v, fn) for k, v in args.items()}
    return fn(args)

but I'm wondering how I could make this quicker. Are there any python tools which could help here, and if not, is this the kind of problem which lends itself well to the use of Cython/C extensions?

Nyps
  • 831
  • 1
  • 14
  • 26
matt
  • 11
  • 1
  • Does this answer your question? [recursion versus iteration](https://stackoverflow.com/questions/15688019/recursion-versus-iteration) – aydow Nov 10 '22 at 13:15
  • 1
    Provide a [mcve] - small but with just enough generality to justify the recursion. – hpaulj Nov 10 '22 at 16:04
  • While recursion is a strong point for python, for something like this where the depth is limited (unless you intentionally make a structure that is infinitely recursive), it may be best. You are already doing iterations, so further casting as iterations may not be possible. `cython` is good for speeding up iterations, but I don't know if it handles recursion any better. `cython` doesn't help when you want to maintain the generality of python objects. – hpaulj Nov 10 '22 at 17:01
  • recursion **isn't** a strong point for python – hpaulj Nov 11 '22 at 08:18
  • @hpaulj what do you mean by 'maintain the generality of python objects'? – matt Nov 11 '22 at 13:05

1 Answers1

0

You could have the behaviour for each args type defined separately as you were doing now, without recursion:

def convert_args(args, fn):
    if isinstance(args, tuple):
        return tuple([fn(i) for i in args])
    elif isinstance(args, list):
        return [fn(i) for i in args]
    elif isinstance(args, dict):
        return {k: fn(v) for k, v in args.items()}
    raise Exception('args must be a list, tuple or dict')

You could make it more obscure yet less verbose by dealing with single-element iterables (list, tuple, set) in one line, and double-element iterables (dicts) in another:

def convert_args(args, fn):
    if isinstance(args, dict):
        return {k: fn(v) for k, v in args.items()}
    elif isinstance(args, (list, tuple, set)):
        return type(args)([fn(i) for i in args])
    raise Exception('args must be a list, tuple, set or dict')
ibarrond
  • 6,617
  • 4
  • 26
  • 45