Uses the `str` correct idiom for working with numbers in Python

I understand that one way to work with the digits of a number in Python is to convert a number into a string, and then use string methods to cut the resulting "number" into groups of "numbers". For example, if I have a prime function that checks for primitiveness, I can confirm that the integer n is both 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 the slice converted back to an integer so that its primitiveness can be checked. Perhaps this is my story with statically typed languages ​​or some kind of vague idea that strings are “expensive”, or experience with languages ​​that include built-in functions for such manipulations (for example, IntegerDigits and FromDigits ); but I wondered whether it was right to solve such problems.

Is converting between sting and numbers the correct or even the only way to access numbers in Python. Are there more effective approaches?

+7
python string type-conversion indexing digits
source share
5 answers

It was always my approach, and it worked fine, although I never did a lot of speed testing. It works especially well when you need to iterate over permutations / combinations of numbers, since you can create such strings using functions in the itertools package.

There are, of course, other methods that include less simple mathematical operations, but if the speed is absolutely low, I feel that the string method is Pythonic itself.


Here, for example, is a more mathematical approach, where a and b are indexed on the right (i.e., one place is 0, tenth place is 1, etc.):

 def getSubdigits(n, a, b): n %= 10 ** a n //= 10 ** b return n 

To do this, to work with the same indexing as string slicing, you first need to find the total number of digits, and the function will look like this:

 def getSubdigits2(n, a, b): l = int(math.ceil(math.log10(n))) n %= 10 ** (l - a) n //= 10 ** (l - b) return n 

And the equivalent of the string:

 def subDigits3(n, a, b): return int(str(n)[a:n]) 

Here are the sync results:

  • subDigits : 0.293327726114
  • subDigits2 : 0.850861833337
  • subDigits3 : 0.990543234267

My contribution from this result is that the slicing method is great if you really don't care about speed, in which case you need to use the first method and think about indexes in a different direction.

+1
source share

In your code example, you can avoid using divmod rather than a string that cuts numbers. divmod(x, y) returns the tuple x//y, x%y , which for y values 10**i is exactly what you want for the left and right parts of your number. It is not necessarily more than Pythonic, although it can be a little faster.

 sn = str(n) all(prime(int(sn[:i])) and prime(int(sn[i:])) for i in range(1, len(sn))) # original all(all(map(prime, divmod(n, 10**i))) for i in range(1, len(sn))) # variant using divmod 

I think that for more general operations with numbers, using str is probably quite reasonable, since doing a lot of math with the strength of your number base will probably be harder to understand than doing things directly on the numbers in a string.

Write code to read if it is really not performance sensitive.

+6
source share

Tense integers in Python are stored in the database with a power of-2, so real conversion is required to convert them to or from decimal notation. In many types of "puzzles" ;-) problems requiring frequent access to decimal digits of very large integers can make the world of difference, and instead use the decimal module. This stores the values ​​in a base with a power of -10, so "converting" to / from decimal digits is a trivial expense.

 >>> import decimal >>> x = decimal.Decimal('1e20') - 1 >>> x Decimal('99999999999999999999') >>> x.as_tuple().digits (9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9) 

It takes time linear in the number of digits. Converting a natural integer to / from a decimal number takes a quadratic time in the number of digits.

Best of all, I can guess about your specific application here, although using divmod() is really the best approach.

+4
source share

How about this?

 def digits(n): if n == 0: yield 0 else: while n: yield n % 10 n //= 10 for digit in digits(123): # do something with digit 

This should be both more convenient and more effective than the example you showed.

EDIT: I want to add two more things.

0) You can expand the technique as needed. Suppose you want to check the correct strokes. Assuming you have is_prime() function:

 def right_trunc_int_values(n): if n == 0: yield 0 else: while n: yield n n //= 10 assert(all(is_prime(n) for n in right_trunc_int_values(317)) 

1) To solve the general problem of convenient work with numbers, you can use the decimal module. I will consider this more. But meanwhile, you can use my digits() function to make an index list of numbers:

 d = list(digits(123)) print(d[2]) # prints 2 

EDIT: It is fairly easy to convert a series of digits to an integer value.

 def int_from_digits(digits): result = 0 found_a_digit = False for digit in reversed(digits): result = result * 10 + digit return result def is_right_trunc_prime(n): d = list(digits(n)) return all(is_prime(int_from_digits(d[:i]) for i in range(len(d), -1, -1))) # example from question of left and right truncatable check d = list(digits(n)) all(prime(int_from_digits(d[:-i])) and prime(int_from_digits(d[i:])) for i in range(1, len(d))) 
+2
source share

Testing left truncatable primes without str() and slicing:

 def is_prime(n): if n < 2: return False elif n == 2: return True elif n % 2 == 0: return False return all(n % x for x in xrange(3,int(pow(n,0.5))+1,2)) def is_left_truncatable_prime(n): largest_power_of_ten = 1 while largest_power_of_ten < n: largest_power_of_ten *= 10 while True: largest_power_of_ten /= 10 # Use // in Python 3 if is_prime(n): n %= largest_power_of_ten if n == 0: return True else: return False print is_left_truncatable_prime(167) # True print is_left_truncatable_prime(173) # True print is_left_truncatable_prime(171) # False 

I did not check this very carefully, so sorry if there are any errors. Let me know if there is, and I will fix them.

EDIT: change the code a bit.

+1
source share

All Articles