I understand that one way to work with the digits of a number in Python is to convert the number to a string, and then use string methods to slice the resulting "number" into groups of "digits". For example, assuming I have a function prime
that tests primality, I can confirm that an integer n is both a left and right truncatable prime with
all(prime(int(str(n)[:-i])) and prime(int(str(n)[i:])) for i in range(1, len(str(n))))
This method involves first converting n to a string so that it can be sliced, and converting that slice back to an integer so that its primality can be checked. Perhaps it's my history with statically typed languages, or some vague idea that strings are "expensive", or experience with languages that include builtin functionality for similar manipulations (like Mathematica's IntegerDigits
and FromDigits
); but I'm left wondering whether this is the right way to go about such tasks.
Is conversion back and forth between stings and numbers the correct — or even only — approach for accessing digits in Python. Are there more efficient approaches?
This has always been my approach and it's worked fine, though I've never done much testing for speed. It works particularly well when needing to iterate over permutations/combinations of digits, since you can build up such strings with the functions in the itertools package.
There are certainly other methods that involve less straightforward mathematical operations, but unless speed is absolutely crucial I feel the string method is the most Pythonic.
Here, for example, is a more mathematical approach, where a and b are indexed from the right (ie ones place is 0, tens place is 1, etc):
def getSubdigits(n, a, b):
n %= 10 ** a
n //= 10 ** b
return n
For this to work with the same indexing as string slicing, you'd need to find the total number of digits first, and the function becomes:
def getSubdigits2(n, a, b):
l = int(math.ceil(math.log10(n)))
n %= 10 ** (l - a)
n //= 10 ** (l - b)
return n
And the string slicing equivalent:
def subDigits3(n, a, b):
return int(str(n)[a:n])
Here's timing results:
subDigits
: 0.293327726114subDigits2
: 0.850861833337subDigits3
: 0.990543234267My takeaway from that result is that the slicing method is fine unless you really care about speed, in which case you need to use the first method and think about the indices in the other direction.