1

Assume there are some useful transformation functions, for example random_spelling_error, that we would like to apply n times.

My temporary solution looks like this:

def reapply(n, fn, arg):
    for i in range(n):
        arg = fn(arg)
    return arg


reapply(3, random_spelling_error, "This is not a test!")

Is there a built-in or otherwise better way to do this?

It need not handle variable lengths args or keyword args, but it could. The function will be called at scale, but the values of n will be low and the size of the argument and return value will be small.

We could call this reduce but that name was of course taken for a function that can do this and too much more, and was removed in Python 3. Here is Guido's argument:

So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly.

Adam Bittlingmayer
  • 1,169
  • 9
  • 22
  • See solutions for R: https://stackoverflow.com/questions/29237136/relooping-a-function-over-its-own-output – Adam Bittlingmayer Aug 26 '17 at 08:25
  • 3
    I don't think there is anything wrong or "un-pythonic" about your loop. Yes, maybe you could do it in one line instead of three, but what for? It's immediately clear what it's supposed to do, making it _very_ pythonic IMHO. – tobias_k Aug 26 '17 at 09:03
  • 1
    @tobias_k I agree it's readable (and efficient) as is, I am just wondering if there is a convention or if I've duplicated something, because the task seems very ordinary. – Adam Bittlingmayer Aug 26 '17 at 09:07

3 Answers3

1

reduce is still available in python 3 using the functools module. I don't really know that it's any more pythonic, but here's how you could achieve it in one line:

from functools import reduce

def reapply(n, fn, arg):
    return reduce(lambda x, _: fn(x), range(n), arg)
DBrowne
  • 683
  • 4
  • 12
  • 1
    Thank you, the loop is more readable and saves the import so in practice I would choose it over this, but this is a good answer for those who are curious how it would be done with `reduce`. – Adam Bittlingmayer Aug 26 '17 at 09:10
1

Get rid of the custom function completely, you're trying to compress two readable lines into one confusing function call. Which one do you think is easier to read and understand, your way:

foo = reapply(3, random_spelling_error, foo)

Or a simple for loop that's one more line:

for _ in range(3):
    foo = random_spelling_error(foo)

Update: According to your comment

Let's assume that there are many transformation functions I may want to apply.

Why not try something like this:

modifiers = (random_spelling_error, another_function, apply_this_too)
for modifier in modifiers:
    for _ in range(3):
        foo = modifier(foo)

Or if you need different amount of repeats for different functions, try creating a list of tuples:

modifiers = [
    (random_spelling_error, 5),
    (another_function, 3),
    ...
]

for modifier, count in modifiers:
    for _ in range(count):
        foo = modifier(foo)
Markus Meskanen
  • 19,939
  • 18
  • 80
  • 119
  • Let's assume that there are many transformation functions I may want to apply. (There are.) There could also be other reasons that the function must be a variable. – Adam Bittlingmayer Aug 26 '17 at 09:54
  • @A.M.Bittlingmayer See the update. I just don't think you should force the future developers (even future yourself) to go read what `reapply` does when it can all be done in a few lines of Python. – Markus Meskanen Aug 26 '17 at 15:46
  • And if you ever *really* do need a function, then your way is the best way, keep it simple. But I don't think you should use a function here. – Markus Meskanen Aug 26 '17 at 15:47
0

some like recursion, not always obviously 'better'

def reapply(n, fn, arg):
    if n:
        arg = reapply(n-1, fn, fn(arg))
    return arg


reapply(1, lambda x: x**2, 2)
Out[161]: 4

reapply(2, lambda x: x**2, 2)
Out[162]: 16
f5r5e5d
  • 3,656
  • 3
  • 14
  • 18
  • 1
    Could be made shorter as `return reapply(...) if n else arg`, but IMHO neither clearer nor better (recursion limit?). – tobias_k Aug 26 '17 at 08:52