views:

271

answers:

4

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?

+3  A: 

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
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
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.
MatrixFrog
+4  A: 

Fastest, cleanest is itertools.repeat:

import itertools

for _ in itertools.repeat(None, n):
    foo()
Alex Martelli
That doesn't get rid of the unused variable.
dan04
I timed a couple methods, and the itertools one is about 50% faster than xrange. I wouldn't say it's any cleaner, though.
perimosocordiae
@dan04, **what** "unused variable" -- **_**?! Puh-Leahze!-)
Alex Martelli
+4  A: 

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 (web-ish version for the PDF averse). 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?"

msw
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
Also, awesome link.
perimosocordiae
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
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
A: 

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