104

Which is more pythonic?

While loop:

count = 0
while count < 50:
    print "Some thing"
    count = count + 1

For loop:

for i in range(50):
    print "Some thing"

Edit: not duplicate because this has answers to determine which is clearer, vs. how to run a range without 'i' -- even though that ended up being the most elegant

Lionel
  • 3,188
  • 5
  • 27
  • 40
  • 2
    Term "Pythonic" is being overused. It's a synonim for "readable" and "easily understandable". In Python, at least. – darioo Nov 24 '10 at 08:25
  • 2
    Possible duplicate of [Is it possible to implement a Python for range loop without an iterator variable?](http://stackoverflow.com/questions/818828/is-it-possible-to-implement-a-python-for-range-loop-without-an-iterator-variable) – Ciro Santilli OurBigBook.com Nov 24 '15 at 21:02
  • The second is clearly more pythonic because you are using ` for in` construct, which is more Pythonic. This isn't just opinion based. Most people know what you mean by "Pythonic" code. – FreelanceConsultant Mar 10 '23 at 20:16
  • Aside from that, to the extent that there is any argument about what's "more pythonic", that's considered off topic nowadays - we explicitly don't want to encourage discussion, because this is **not a discussion forum**. People who end up here from a search engine want to know how to make a loop run a specific number of times; we have a proper canonical for that, and this is not it. – Karl Knechtel Mar 10 '23 at 23:30

5 Answers5

145

Personally:

for _ in range(50):
    print "Some thing"

if you don't need i. If you use Python < 3 and you want to repeat the loop a lot of times, use xrange as there is no need to generate the whole list beforehand.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 18
    Watch out for _ being mapped to the gettext translation function though. – Gintautas Miliauskas Nov 24 '10 at 08:20
  • Thanks for this answer; this was the main reason I wasn't using the for-loop because I had an unused variable in "i". – Lionel Nov 26 '10 at 19:20
  • 7
    _ is just like any other variable. It's only in the REPL that it has any particular significance. The OP may as well stick with `i`. – vezult Dec 14 '12 at 14:26
  • 2
    @vezult I like this as it makes it clear that the variable is not being used in the statement. Is there perhaps a reason that overshadows this to stick with the `i`? – ryanjdillon Nov 28 '13 at 15:32
  • 7
    I'm a firm believer in adding ponies, espepcially when it sounds appropriate... for pony in range(50): print("neigh") #python 3 – Paul May 21 '14 at 05:57
  • @Paul I second the ponies. – Jonathan Komar May 22 '17 at 05:47
  • I third @Paul for the ponies, Not only is that hilarious, but, this also protects you from the gettext translation issue as Gintautas mentioned. – JayRizzo Sep 02 '18 at 08:31
2

The for loop is definitely more pythonic, as it uses Python's higher level built in functionality to convey what you're doing both more clearly and concisely. The overhead of range vs xrange, and assigning an unused i variable, stem from the absence of a statement like Verilog's repeat statement. The main reason to stick to the for range solution is that other ways are more complex. For instance:

from itertools import repeat

for unused in repeat(None, 10):
    del unused   # redundant and inefficient, the name is clear enough
    print "This is run 10 times"

Using repeat instead of range here is less clear because it's not as well known a function, and more complex because you need to import it. The main style guides if you need a reference are PEP 20 - The Zen of Python and PEP 8 - Style Guide for Python Code.

We also note that the for range version is an explicit example used in both the language reference and tutorial, although in that case the value is used. It does mean the form is bound to be more familiar than the while expansion of a C-style for loop.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
  • Would it not be better to use the repeated thing directly, ie: `for s in repeat('This is run 10 times', 10): print s` ?? – F1Rumors May 10 '16 at 03:39
  • Certainly! But the print in the sample code was only an example of a repeated section of code, for which there may not be a central object. – Yann Vernier May 10 '16 at 11:50
  • Python core developer says this is faster than using `range()` https://twitter.com/raymondh/status/1144527183341375488 – Chris_Rands Jun 28 '19 at 08:55
  • It is indeed faster, because it doesn't need to look up or create a different `int` object for each iteration. However, programmer time may be more valuable than execution time. – Yann Vernier Jun 28 '19 at 11:34
2

If you are after the side effects that happen within the loop, I'd personally go for the range() approach.

If you care about the result of whatever functions you call within the loop, I'd go for a list comprehension or map approach. Something like this:

def f(n):
    return n * n

results = [f(i) for i in range(50)]
# or using map:
results = map(f, range(50))
knutin
  • 5,033
  • 19
  • 26
-7

There is not a really pythonic way of repeating something. However, it is a better way:

map(lambda index:do_something(), xrange(10))

If you need to pass the index then:

map(lambda index:do_something(index), xrange(10))

Consider that it returns the results as a collection. So, if you need to collect the results it can help.

JayRizzo
  • 3,234
  • 3
  • 33
  • 49
  • Not only is this not really better (function call overhead, lesser known lambda expressions, collecting unused results in a list), 10 is not an iterable. – Yann Vernier Dec 15 '14 at 11:32
  • Yes, xrange(10) not 10. I said it is better because you do not need to write a function or make a loop. However, as I said there is not a real pythonic way. I changed the code, Thanks. – Abi M.Sangarab Apr 29 '15 at 10:23
-7

How about?

while BoolIter(N, default=True, falseIndex=N-1):
    print 'some thing'

or in a more ugly way:

for _ in BoolIter(N):
    print 'doing somthing'

or if you want to catch the last time through:

for lastIteration in BoolIter(N, default=False, trueIndex=N-1):
    if not lastIteration:
        print 'still going'
    else:
        print 'last time'

where:

class BoolIter(object):

    def __init__(self, n, default=False, falseIndex=None, trueIndex=None, falseIndexes=[], trueIndexes=[], emitObject=False):
        self.n = n
        self.i = None
        self._default = default
        self._falseIndexes=set(falseIndexes)
        self._trueIndexes=set(trueIndexes)
        if falseIndex is not None:
            self._falseIndexes.add(falseIndex)
        if trueIndex is not None:
            self._trueIndexes.add(trueIndex)
        self._emitObject = emitObject


    def __iter__(self):
        return self

    def next(self):
        if self.i is None:
            self.i = 0
        else:
            self.i += 1
        if self.i == self.n:
            raise StopIteration
        if self._emitObject:
            return self
        else:
            return self.__nonzero__()

    def __nonzero__(self):
        i = self.i
        if i in self._trueIndexes:
            return True
        if i in self._falseIndexes:
            return False
        return self._default

    def __bool__(self):
        return self.__nonzero__()
DangerMouse
  • 704
  • 7
  • 20