Digital root computation with benchmarking I've read this question about computing the digital root of some integer, and wondered how it would look like in Python.

As a reminder,


  "If you take the digits of any number and add them together, and then add the digits of the resulting number together, and continue doing that until you get a single digit, that single digit is the digital root of the original number."


This is not much about the algorithm performance itself, since it was shown in this answer that instead of summing the digits, you could do a simple division by 9 to get the same result. The question is more about the readability, elegance, and pythonicity (if this word exists) of my code. I checked its correctness with a tool from Thonky.

More, I'd be interested in knowing your opinion on the way I deal with sys.argv, the user interaction, and the benchmarking.

Finally, is it OK to use the int type for such long numbers? It seems to work fine even with 1000-digit numbers, but intuitively it shouldn't...

Any suggestion would be highly appreciated. Thanks.



digitalroot.py

# -*- encoding: utf-8 -*-

import time
import random
import sys

def request_user_number():
    while True:
        try:
            nb = int(raw_input('Enter a number\n'))
            return nb
        except (ValueError, NameError):
            print 'This is not a number. Try again.'

def request_user_again():
    while True:
        try:
            ans = str(raw_input('New computation? (y/n)\n')).lower()
            if ans[0] == 'n':
                again = False
                print 'Bye !'
                break
            elif ans[0] == 'y':
                again = True
                break
            else:
                print 'Did not understand. Try again.'
        except IndexError:
            print 'Say something.'
    return again

def digits_generator(nb):
    tmp = nb
    while tmp != 0:
        yield tmp % 10
        tmp /= 10

def digital_root(nb):
    if nb &lt; 10:
        return nb
    else:
        s = 0
        for d in digits_generator(nb):
            s += d
        return digital_root(s)

def usage():
    print 'Wrong usage'
    print '\'python digitalroot.py ui\' for a user chosen number'
    print '\'python digitalroot.py benchmark nb_size tries\' for benchmarking'
    exit(1)

if __name__ == '__main__':
    if len(sys.argv) &lt; 2 or sys.argv[1] not in ['ui', 'benchmark']:
        usage()
    if sys.argv[1] == 'ui':
        again = True
        while again:
            nb = request_user_number()
            print 'Computing digital root of', nb
            start_time = time.clock()
            dr = digital_root(nb)
            end_time = time.clock()
            print nb, '--&gt;', dr, '(time :', end_time-start_time, 's)'
            again = request_user_again()
    elif sys.argv[1] == 'benchmark':
        try:
            nb_size = int(sys.argv[2])
            tries = int(sys.argv[3])
        except (ValueError, NameError, IndexError):
            usage()
        global_start = time.clock()
        s = 0
        for i in xrange(tries):
            nb = random.randint(10**(nb_size-1), 10**nb_size-1)
            start_time = time.clock()
            d = digital_root(nb)
            end_time = time.clock()
            s += end_time-start_time
            if random.randint(1, tries/5) == 1:
                print 'Random control : i =', i, ',', nb, '--&gt;', d
        print nb_size, 'digits,', tries, 'tries'
        print s, 'seconds (average =', float(s)/tries, 's/try)'
        print 'Total benchmark time :', time.clock()-global_start
I think your algorithmic part is exceedingly verbose.  One of the nicenesses of Python is how it can lead to code that reads almost like English.  And for this particular problem, even leaving performance considerations aside, I think iteration is clearer than recursion.  So I would rewrite things as:

def digits_of(number):
    """Yields the digits of an integer."""
    while number &gt; 0:
        yield number % 10
        number //= 10

def digital_root(number):
    """Computes the digital root of an integer."""
    while number &gt; 9:
        number = sum(digits_of(number))
    return number


I'm pretty sure my 6 year old daughter could make sense of that last function with very little help.