49

Say I have a function foo that I want to call n times. In Ruby, I would write:

n.times { foo }

In Python, I could write:

for _ in xrange(n): foo()

But that seems like a hacky way of doing things.

My question: Is there an idiomatic way of doing this in Python?

perimosocordiae
  • 17,287
  • 14
  • 60
  • 76
  • 4
    Don't forget that ruby also computes the counter variable for the loop. In particular n.times{|i| foo}. In your case you are just discarding it. So why is this ok in ruby but hacky in python? – Andriy Drozdyuk Apr 17 '10 at 03:34
  • 7
    Because I don't have to throw it away explicitly. :-) The Ruby version also demonstrates the intent of the line better, IMO. – perimosocordiae Apr 17 '10 at 03:52
  • I use x as the throw away variable, but `_` seems more idiomatic. And yes, python doesn't have a `times` function .. – hasen Apr 17 '10 at 04:05
  • 1
    @perimosocoriade if you are concerned about "demonstrating your intent" clearly to other python developers, the `_` is the way to go. – TM. Apr 17 '10 at 04:28
  • Although is somehow idiomatic, I really don't like the `_` variable. I find it confusing. I prefer to use `i`, usually, but maybe because I used to program a lot in C... – Khelben Apr 17 '10 at 06:12
  • @Khelben: Using meaningful names to throwaway variables is a bad idea, IMO. It's a kind of promise, that this variable will be used somewhere, which could be misleading. – gorsky Apr 17 '10 at 12:13
  • `for x in iterator` is as simple as it gets in Python. I can't think of any way to simplify it further. – jathanism Apr 17 '10 at 18:01
  • 1
    @drozzy part of the reason ruby blocks dont enforce arity is so you can throwaway variables exactly as in this example -- it's not going to hurt performance, and it's idiomatic. Nothing hacky about it. – horseyguy Jun 24 '13 at 04:18

4 Answers4

44

You've already shown the idiomatic way:

for _ in range(n): # or xrange if you are on 2.X
    foo()

Not sure what is "hackish" about this. If you have a more specific use case in mind, please provide more details, and there might be something better suited to what you are doing.

TM.
  • 108,298
  • 33
  • 122
  • 127
  • 3
    I only think it's hackish because it's generating the range values that I throw away with the _ convention. It seems like I'm sidestepping, for some reason. – perimosocordiae Apr 17 '10 at 03:37
  • That's why you use `_` instead of some other name. It means "throw away variable" -- the idea of `_` as a throw-away is common to Python, Prolog, Haskell, and presumably some other languages. – Tyler Apr 17 '10 at 04:18
  • 2
    Is range not inefficient because it uses n bytes of memory for the counter, whereas a C for loop would use only one byte for the counter? – bcoughlan Sep 02 '11 at 00:05
  • +1 for the use of `_`, didn't know that one! – Jacob Tomlinson Jan 06 '14 at 15:26
  • This should be the accepted answer. – B Seven Feb 22 '19 at 18:36
  • The very use of _ in Python is a bit hackish. In some other languages that's a syntactic construct that ignores any value associated (and thus immediately allows the compiler to optimise it away and GC to release any object passed to it). In Python it's a real variable, just with the convention that it is not used for anything else in your application logic. Since Python For-loop interator variables persist after the loop terminates, this is particularly hacky. – itsbruce Dec 20 '20 at 22:06
18

If you want the times method, and you need to use it on your own functions, try this:

def times(self, n, *args, **kwargs):
    for _ in range(n):
        self.__call__(*args, **kwargs)

import new
def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)
    return func

now add a @repeatable decorator to any method you need a times method on:

@repeatable
def foo(bar):
    print bar

foo.times(4, "baz") #outputs 4 lines of "baz"
Carson Myers
  • 37,678
  • 39
  • 126
  • 176
16

Fastest, cleanest is itertools.repeat:

import itertools

for _ in itertools.repeat(None, n):
    foo()
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
10

The question pre-supposes that calling foo() n times is an a priori necessary thing. Where did n come from? Is it the length of something iterable? Then iterate over the iterable. As I am picking up Python, I find that I'm using few to no arbitrary values; there is some more salient meaning behind your n that got lost when it became an integer.

Earlier today I happened upon Nicklaus Wirth's provocative paper for IEEE Computer entitled Good Ideas - Through the Looking Glass (archived version for future readers). In section 4 he brings a different slant on programming constructs that everyone (including himself) has taken for granted but that hold expressive flaws:

"The generality of Algol’s for statement should have been a warning signal to all future designers to always keep the primary purpose of a construct in mind, and to be weary of exaggerated generality and complexity, which may easily become counter-productive."

The algol for is equivalent to the C/Java for, it just does too much. That paper is a useful read if only because it makes one not take for granted so much that we so readily do. So perhaps a better question is "Why would you need a loop that executes an arbitrary number of times?"

Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
msw
  • 42,753
  • 9
  • 87
  • 112
  • 5
    You're right, there are very few cases where this is needed. There are some, though. In my case, I needed to skip a set number of lines through a file before reading data. Also, benchmarking code tends to have a need for arbitrary numbers of runs. Things that affect some global state (like IO operations) are the most common culprits. – perimosocordiae Apr 17 '10 at 04:26
  • If you want to skip a set number of lines from a file you could use: next(itertools.islice(thefile, n-1, n)) This has the advantage of speed, though it lacks the clarity of the for loop. – Duncan Apr 17 '10 at 12:54
  • I awoke dreaming about the wisdom of writing `class skipfile(file)` with `skipfile(path, skiplines=n)` and `skipfile.skip(lines=m)` and I blame you, perimosocordiae ;) – msw Apr 17 '10 at 13:04
  • 1
    Sadly, those IEEE links are now broken. The article is available for subscribers, or to purchase, from http://www.computer.org/portal/web/csdl/doi/10.1109/MC.2006.20. – Sam Dutton Nov 28 '10 at 14:04
  • @SamDutton Unfortunately that link is broken too – Josh K Dec 28 '12 at 23:57
  • @msw Any chance you have a PDF copy saved that you can replace the links to? – Josh K Dec 28 '12 at 23:57
  • 1
    @perimosocordiae: you could skip `n` lines in a file using [`consume()` itertools' recipe: `next(islice(file, n, n), None)`](http://docs.python.org/2/library/itertools.html#recipes) – jfs Mar 08 '13 at 01:15
  • 8
    I agree with the sentiment, but I just don't see how this answers the question... – jxstanford Aug 16 '14 at 03:47
  • Weary? I'm tired of it already. – Russia Must Remove Putin Aug 05 '15 at 20:43
  • "Why would you need a loop that executes an arbitrary number of times?" Here's why I need to: I'm instructing the computer to read data from an instrument N times. I must call a read_data() function that number of times. If there is a way to complete tasks like that by iterating through an existing array, I'm all ears! – Julian Irwin Jan 19 '16 at 20:28
  • I want to draw a hilbert curve, so I'm writing an lsystem, the lsystem needs to apply the rules for each iteration. I don't know how many iterations I want it to do, enough that it's interesting, not so many that it's drawing off the edge of the screen. I want to try 5 iterations and adjust from there by eyeballing it. – Joshua Cheek Jan 05 '19 at 01:29
  • I appreciate the general sentiment, but it doesn't really answer the question. Despite being somebody who prefers FP and so isn't fond of iterative loops, I do think Ruby's times method is very simple and expressive (which make your Wirth comment just wrong as an answer) and appropriate for an imperative language. It is the OO equivalent of Church notation for integers in FP: "What are numbers for in code? For doing something N times". – itsbruce Dec 20 '20 at 17:50
  • Python can't do the Ruby "times" thing because it doesn't have code blocks. The Ruby times metaphor is actually quite a good one for those occasions where it is actually appropriate. This is only one example of how the lack of code blocks hampers Python's expressiveness. – itsbruce Dec 20 '20 at 17:53