64

Is there a short way to call a function twice or more consecutively in Python? For example:

do()
do()
do()

maybe like:

3*do()
Georgy
  • 12,464
  • 7
  • 65
  • 73
alwbtc
  • 28,057
  • 62
  • 134
  • 188
  • 4
    Note that `3 * do()` is a valid Python expression with a very well defined result: it does multiply the return value of calling `do` once by 3. It would be possible, however, to write a decorator to enable one to write things such as (3 * do)() - with a variante of the answer at http://stackoverflow.com/questions/8998997/product-of-two-functions/9001894#9001894 – jsbueno Jan 28 '12 at 23:16
  • 1
    I got here because I was wondering if there were a way to do this similar to what you suggested... It is probably not used enough to warrant a new "function calling multiplier operator", though, given that using it instead of a for loop only saves about 17 keystrokes. – Chelonian Jul 15 '14 at 05:46
  • 1
    Adding syntactic sugar in a programming language like `3*do()` is never a good thing. Will cause a lot of problems and issues later, not even mentioning that it has a totally different meaning in this context. – tonga Sep 01 '15 at 17:56
  • 2
    always interesting to see the different opinions on syntactical sugar.. Some dislike it, others embrace it. we get the python opinion (well from one voice anyway) above.. meanwhile in ruby you have `2.times do {block}` (NOT an attempt to make a 'ruby is better' argument, just noting the differences in how this is viewed by different coding communities) – Chuck van der Linden Jun 13 '18 at 23:23

9 Answers9

113

I would:

for _ in range(3):
    do()

The _ is convention for a variable whose value you don't care about.

You might also see some people write:

[do() for _ in range(3)]

however that is slightly more expensive because it creates a list containing the return values of each invocation of do() (even if it's None), and then throws away the resulting list. I wouldn't suggest using this unless you are using the list of return values.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 23
    I think it's never a good idea to use a list comprehension just to repeat actions – juliomalegria Jan 28 '12 at 19:58
  • 1
    That's true. I should be more explicit that I'm not actually *recommending* that. – Greg Hewgill Jan 28 '12 at 19:59
  • 2
    Yeah, List Comprehensions are meant to create new lists and should not be used for side-effects. – g.d.d.c Jan 28 '12 at 21:13
  • If you're interested in an aggregate of the results then a generator comprehension inside the aggregating function (like `sum` for example) works nicely: `sum(do() for _ in range(3))` or `sum(1 for _ in range(3) if do() > 3)` for conditional counting, etc. – flutefreak7 Nov 19 '15 at 23:25
  • @flutefreak7 List comprehension is functional, and the other is procedural: a method with name `do` should be a procedure (side effects and no return value). Therefore do not use a list comprehension for this. However if it is a function, then first name it as one (use a noun that describes what it returns, or adjective if returning a boolean), and use a list comprehension. – ctrl-alt-delor Apr 13 '21 at 08:32
22

You could define a function that repeats the passed function N times.

def repeat_fun(times, f):
    for i in range(times): f()

If you want to make it even more flexible, you can even pass arguments to the function being repeated:

def repeat_fun(times, f, *args):
    for i in range(times): f(*args)

Usage:

>>> def do():
...   print 'Doing'
... 
>>> def say(s):
...   print s
... 
>>> repeat_fun(3, do)
Doing
Doing
Doing
>>> repeat_fun(4, say, 'Hello!')
Hello!
Hello!
Hello!
Hello!
juliomalegria
  • 24,229
  • 14
  • 73
  • 89
12

Three more ways of doing so:

(I) I think using map may also be an option, though is requires generation of an additional list with Nones in some cases and always needs a list of arguments:

def do():
    print 'hello world'

l=map(lambda x: do(), range(10))

(II) itertools contain functions which can be used used to iterate through other functions as well https://docs.python.org/2/library/itertools.html

(III) Using lists of functions was not mentioned so far I think (and it is actually the closest in syntax to the one originally discussed) :

it=[do]*10
[f() for f in it]

Or as a one liner:

[f() for f in [do]*10]
VDV
  • 874
  • 9
  • 12
8

A simple for loop?

for i in range(3):
  do()

Or, if you're interested in the results and want to collect them, with the bonus of being a 1 liner:

vals = [do() for _ in range(3)]
g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
5

My two cents:

from itertools import repeat 

list(repeat(f(), x))  # for pure f
[f() for f in repeat(f, x)]  # for impure f
Joffer
  • 1,921
  • 2
  • 21
  • 23
  • 2
    Could you elaborate a bit on the difference between pure and impure in this context ? – Mr_and_Mrs_D Jun 09 '17 at 18:57
  • 1
    sure -- say you have an 'impure' function that just prints and does nothing else: if you want it to print x times you'd use the second approach; the first would only lead to one printing. On the other hand, if you have a pure function, which executes no side effects, you could use either approach, but the first would only result in one application of the function, which could be significantly cheaper. – Joffer Jun 12 '17 at 21:24
  • 1
    `list(repeat(f(), x))` doesn't repeat the function call but the function result (as mentioned in the explanation of "pure/impure f"). Therefore not what the OP asked for (which was calling the function multiple times). – rlat Jun 23 '20 at 13:34
  • 1
    @rlat true, but then again why call it more than once if it's always going to give the same result and has no side effects? – Joffer Oct 12 '20 at 18:41
4

Here is an approach that doesn't require the use of a for loop or defining an intermediate function or lambda function (and is also a one-liner). The method combines the following two ideas:

Putting these together, we get:

next(islice(iter(do, object()), 3, 3), None)

(The idea to pass object() as the sentinel comes from this accepted Stack Overflow answer.)

And here is what this looks like from the interactive prompt:

>>> def do():
...   print("called")
... 
>>> next(itertools.islice(iter(do, object()), 3, 3), None)
called
called
called
Community
  • 1
  • 1
cjerdonek
  • 5,814
  • 2
  • 32
  • 26
2
from itertools import repeat, starmap

results = list(starmap(do, repeat((), 3)))

See the repeatfunc recipe from the itertools module that is actually much more powerful. If you need to just call the method but don't care about the return values you can use it in a for loop:

for _ in starmap(do, repeat((), 3)): pass

but that's getting ugly.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
1

You can use itertools.repeat with operator.methodcaller to call the __call__ method of the function N times. Here is an example of a generator function doing it:

from itertools import repeat
from operator import methodcaller


def call_n_times(function, n):
    yield from map(methodcaller('__call__'), repeat(function, n))

Example of usage:

import random
from functools import partial

throw_dice = partial(random.randint, 1, 6)
result = call_n_times(throw_dice, 10)
print(list(result))
# [6, 3, 1, 2, 4, 6, 4, 1, 4, 6]
Georgy
  • 12,464
  • 7
  • 65
  • 73
0

You may try while loop as shown below;

def do1():
    # Do something

def do2(x):
    while x > 0:
        do1()
        x -= 1

do2(5)

Thus make call the do1 function 5 times.

Kerwin Sneijders
  • 750
  • 13
  • 33
Alphard
  • 25
  • 3