19
\$\begingroup\$

This is not a school project, just something to help me learn some Python! Just looking to see what I can improve on, looking for tips to reduce size & make it a bit easier for the computer to run it.

suffixes = ["", "Thousand", "Million", "Billion", "Trillion", "Quadrillion","Quintillion", "Sextillion","Septillion","Octillion","Nonillion", "Decillion", "Undecillion","Duodecillion", "Tredecillion", "Quattuordecillion","Quindecillion","Sexdecillion","Septendecillion","Octodecillion", "Novemdecillion","Vigintillion","Unvigintillion","Duovigintillion","Trevigintillion","Quattuorvigintillion","Quinvigintillion", "Sexvigintillion","Septenvigintillion","Octovigintillion","Novemvigintillion","Trigintillion", "Untrigintillion", "Duotrigintillion","Quattuortrigintillion","Quintrigintillion", "Sextrigintillion","Septentrigintillion", "Octotrigintillion", "Novemtrigintillion","Quadragintillion", "Unquadragintillion", "Duoquadragintillion", "Trequadragintillion","Quattuorquadragintillion", "Quinquadragintillion","Sexquadragintillion","Septenquadragintillion", "Octoquadragintillion", "Novemquadragintillion","Quinquagintillion", "Unquinquagintillion", "Duoquinquagintillion", "Trequinquagintillion","Quattuorquinquagintillion","Quinquinquagintillion", "Sexquinquagintillion","Septenquinquagintillion", "Octoquinquagintillion", "Novemquinquagintillion","Sexagintillion", "Unsexagintillion", "Duosexagintillion", "Tresexagintillion","Quattuorsexagintillion", "Quinsexagintillion","Sexsexagintillion","Septensexagintillion", "Octosexagintillion", "Novemsexagintillion","Septuagintillion", "Unseptuagintillion", "Duoseptuagintillion", "Treseptuagintillion","Quattuorseptuagintillion", "Quinseptuagintillion","Sexseptuagintillion","Septenseptuagintillion", "Octoseptuagintillion", "Novemseptuagintillion","Octogintillion", "Unoctogintillion", "Duooctogintillion", "Treoctogintillion","Quattuoroctogintillion", "Quinoctogintillion","Sexoctogintillion","Septenoctogintillion","Octooctogintillion","Novemoctogintillion","Nonagintillion","Unnonagintillion","Duononagintillion","Trenonagintillion","Quattuornonagintillion","Quinnonagintillion","Sexnonagintillion","Septennonagintillion", "Octononagintillion", "Novemnonagintillion""Centillion", "Uncentillion", "Duocentillion", "Trecentillion", "Quattuorcentillion", "Quincentillion", "Sexcentillion","Septencentillion", "Octocentillion", "Novemcentillion","Primovegesimocent", "Unprimovegesimocent", "Duoprimovegesimocent", "Treprimovegesimocent", "Quattuorprimovegesimocent","Quinprimovegesimocent", "Sexprimovegesimocent", "Septenprimovegesimocent", "Octoprimovegesimocent", "Novemprimovegesimocent","Ducent", "Unducent", "Duoducent", "Treducent", "Quattuorducent", "Quinducent", "Sexducent", "Septenducent", "Octoducent", "Novemducent","Trecent", "Untrecent", "Duotrecent", "Tretrecent", "Quattuortrecent", "Quintrecent", "Sextrecent", "Septentrecent", "Octotrecent","Novemtrecent","Duotrigintatrecent", "Unduotrigintatrecent", "Duoduotrigintatrecent", "Treduotrigintatrecent", "Quattuorduotrigintatrecent","Quinduotrigintatrecent", "Sexduotrigintatrecent", "Septenduotrigintatrecent", "Octoduotrigintatrecent", "Novemduotrigintatrecent","Quadringent", "Unquadringent", "Duoquadringent", "Trequadringent", "Quattuorquadringent", "Quinquadringent", "Sexquadringent","Septenquadringent", "Octoquadringent", "Novemquadringent","Quingent", "Unquingent", "Duoquingent", "Trequingent", "Quattuorquingent", "Quinquingent", "Sexquingent", "Septenquingent","Octoquingent", "Novemquingent","Sescent", "Unsescent", "Duosescent", "Tresescent", "Quattuorsescent", "Quinsescent", "Sexsescent", "Septensescent", "Octosescent","Novemsescent","Septuagintisescent", "Unseptuagintisescent", "Duoseptuagintisescent", "Treseptuagintisescent", "Quattuorseptuagintisescent","Quinseptuagintisescent", "Sexseptuagintisescent", "Septenseptuagintisescent", "Octoseptuagintisescent", "Novemseptuagintisescent","Septingent", "Unseptingent", "Duoseptingent", "Treseptingent", "Quattuorseptingent", "Quinseptingent", "Sexseptingent","Septenseptingent", "Octoseptingent", "Novemseptingent","Octingent", "Unoctingent", "Duooctingent", "Treoctingent", "Quattuoroctingent", "Quinoctingent", "Sexoctingent", "Septenoctingent","Octooctingent", "Novemoctingent","Nongent", "Unnongent", "Duonongent", "Trenongent", "Quattuornongent", "Quinnongent", "Sexnongent", "Septennongent", "Octonongent","Novemnongent"]
# Get the user's input number
number = int(input("Enter a number "))
# Turn the int number into a string and format with ,'s
number = str("{:,}".format(number))
# For loop to find the amount of commas in the newly made string
commas = 0
x = 0
while x < len(number):
    if number[x] == ',':
        commas += 1
    x += 1
# Use the amount of commas to decide the element in the array that will be used as a suffix
# for example, if there are 2 commas it will use million.
print(number.split(',')[0], suffixes[commas])
# Wait for the user's input before closing so that you don't need to run it from a terminal
input()
\$\endgroup\$
  • 2
    \$\begingroup\$ What about: number = input("Enter a number ") and then using len(number)//3 ? \$\endgroup\$ – flen Dec 15 '17 at 2:34
  • 2
    \$\begingroup\$ That only works if the user doesn't enter any leading whitespace, sign, base indicator, or trailing whitespace. \$\endgroup\$ – Snowbody Dec 15 '17 at 13:32
  • \$\begingroup\$ What about making the code so that if you put 3846372 in it will say three million eight hundred and forty six thousand three hundred and seventy two, that would be another interesting question \$\endgroup\$ – 13ros27 Dec 16 '17 at 16:54
  • \$\begingroup\$ @Snowbody you're right, for this to work one must use len(str(int(number))) //3 \$\endgroup\$ – flen Dec 17 '17 at 6:37
17
\$\begingroup\$

I'll put my comments inline.

  • suffixes = ["", "Thousand", "Million", "Billion", "Trillion", "Quadrillion",  ...
    

    This line is way too long. The standard for Python code, PEP-8, says that line length should be kept under 79 characters. Perhaps it would be better to put these in a file and read them in.

    However, note that there is a regular pattern from decillion up to novemnonagintillion, and another regular pattern above that. This might be able to be represented in code instead of exhaustively listing everything.

  • # Get the user's input number
    number = int(input("Enter a number "))
    

    This code will raise an exception if the user's input can't be converted to a number. I don't think that's what you want.

  • # Turn the int number into a string and format with ,'s
    number = str("{:,}".format(number))
    

    What's the purpose of the str() function? The format() method already returns a str.

  • # For loop to find the amount of commas in the newly made string
    commas = 0
    x = 0
    while x < len(number):
        if number[x] == ',':
            commas += 1
        x += 1
    

    Python programmers try to avoid for and while loops, particularly when dealing with sequences. This actually leads to easier-to-read and less error prone code. And, guess what, strings are sequences. The built-in count(item) method of sequences finds out how many copies of item there are in a sequence, so finding the comma-count would be number.count(',') (though as others have posted, there are easier ways to find the magnitude of the number). Me, I'd try to avoid converting the number back to a string, and use math.log10(number) to get the number of decimals.

  • # Use the amount of commas to decide the element in the array that will be used as a suffix
    # for example, if there are 2 commas it will use million.
    print(number.split(',')[0], suffixes[commas])
    

    If you just want to pull out the first element, no need to do the whole split. You can use number.partition(',')[0] or number[:number.find(',')] or, probably the simplest, number.split(',', maxsplit=1)[0]. (The 1 as the maxsplit parameter means "stop after the first separator".) Don't make the processor do unnecessary work.

Good job on challenging yourself!

\$\endgroup\$
  • \$\begingroup\$ Excellent comments! And math.log10(number) was a smart move \$\endgroup\$ – flen Dec 15 '17 at 6:32
  • \$\begingroup\$ I have coded the second point to demonstrate it \$\endgroup\$ – 13ros27 Dec 15 '17 at 18:39
  • 1
    \$\begingroup\$ One more caveat: int(123.456) in the code will silently truncate the float to 123, which may not be what the OP wanted \$\endgroup\$ – flen Dec 17 '17 at 11:25
7
\$\begingroup\$

This approach is based on len(input()).

As @Snowbody pointed out, we must be sure that it works even if the user inputs any leading whitespace, sign, base indicator, or trailing whitespace.

Getting the input

The easiest way for it would be trying str(int(input())), which is inefficient:

#Beware that int(float) will truncate the float to an int, which might not be desired
while True:
    number = input('Enter an int: ')
    try:
        is_negative = number.lstrip().startswith("-")
        number = str(int(number))
    except: #could be ValueError, OverflowError, and what else for int()?
        print("You didn't input an int! Let's try again")
        continue
    break

The second easiest way would be to disregard any input that is not a decimal:

while True:
    number = input('Enter an int: ')
    number = number.strip()
    is_negative = number.startswith("-")
    if is_negative or number.startswith("+"):
        number = number[1:].lstrip()

    if number.isdecimal():
        break

    print("You didn't input only decimals! Let's try again")
    continue

This third way seems perfect, though I don't fully understand the function in it (could someone explain it...?)

def isInt_str(v):
    return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit() #one might want to change this to .isdecimal()

while True:
    number = input('Enter an int: ')
    number = number.strip()
    is_negative = number.startswith(-)

    if isInt_str(number):
        break

A fourth way that also works for 0x123, 0b0101, 1.1e+1 and 11e-1 would be this messy code, that needs improvement:

#Please improve me!
while True:
    #This is for Python3, since Python2 can have long(123L)
    number = input('Enter an int: ')
    if not number:
        continue
    if number[0].isspace() or number[-1].isspace():
        number = number.strip()

    is_negative = number.startswith('-')
    if is_negative or number.startswith("+"):
        number = number[1:]
    if number[0].isspace():
        number = number.lstrip()
    if number.isdecimal():
        break

    #I got lazy here, instead of analyzing the string
    if number[2:].isdecimal():
        if number.startswith('0x'):
            try:
                number = str(int(number,16))
                print("Converting from hexadecimal to int")
                break
            except ValueError:
                print("You didn't input an int! Use only decimals. Let's try again")                
                continue
        elif number.startswith('0b'):
            try:
                number = str(int(number,2))
                print("Converting from binary to int")
                break
            except ValueError:
                print("You didn't input an int! Use only decimals. Let's try again")                
                continue

    if number.count('.') == 1:
        if number[-1] == '.':
            print("Converting float input to integer")
            number = number[:-1]
            break
        only_zeros = True
        for x in number[number.find('.')+1:]:
            if x != '0':
                print("You didn't input an int! Did you input a float?")
                only_zeros = False
                break
        if only_zeros:
                print("Converting float input to integer")
                number = number[:number.find('.')]
                break

    #for numbers like 123e+45 or 123e-45:
    if number.count('e+') == 1 and number.count('.') < 2:
        wrong_input = False
        for x in number:
            if x not in '0123456789.e+':
                wrong_input = True
                break
        if wrong_input:
            print("You didn't input an int! Did you input a float?")
            continue
        else:
            try:
                number = str(eval(number))
                if number.count('.') and int(number[number.find('.')+1:]) == 0:
                    break
            except:
                print("You didn't input an int! Did you input a float or infinite?")
                continue
    if number.count('e-') == 1 and number.count('.') < 2:
        wrong_input = False
        for x in number:
            if x not in '0123456789.e-':
                wrong_input = True
                break
        if wrong_input:
            print("You didn't input an int! Did you input a float?")
            continue
        else:
            try:
                number = str(eval(number))
                if number.count('.') and int(number[number.find('.')+1:]) == 0:
                    break
            except:
                print("You didn't input an int! Did you input a float or infinite?")
                continue

    print("You didn't input an int! Use only decimals. Let's try again")
    #this code is WET and should be DRY

We have the input!

Next, it seems to me that you are simply counting 3 decimal places when you run your while loop (accidentally mislabeled as a "for loop").

It would be faster to do this instead, to get the same result:

# How many triads of digits does the number have?
# How many first numbers do you want? 0 means you want all first 3
(how_many_3s, how_many_first_numbers) = divmod(len(number), 3)

first_numbers = number[:(how_many_first_numbers - 1) % 3 + 1]

print("negative "+ first_numbers if is_negative else "" + first_numbers, suffixes[how_many_3s] )
\$\endgroup\$
  • 2
    \$\begingroup\$ You should probably check for a negative sign, but other than that this is a big improvement. \$\endgroup\$ – Oscar Smith Dec 15 '17 at 2:50
  • \$\begingroup\$ You're right about the sign, thanks! I'll fix that, just saw also that the OP wanted something else as the first argument of the print, gonna fix it also \$\endgroup\$ – flen Dec 15 '17 at 2:58
  • \$\begingroup\$ @OscarSmith I saw you improved the code! But I think the OP doesn't want only one of the first digits, it may be up to 3 first digits, so I edited in these changes to reflect that. Great idea to include the is_negative check \$\endgroup\$ – flen Dec 15 '17 at 4:42
  • 2
    \$\begingroup\$ If you are going to compute both a // b and a % b why not do them both at the same time with the divmod(a,b) function? \$\endgroup\$ – Snowbody Dec 15 '17 at 5:50
  • 2
    \$\begingroup\$ Check out the first answer about asking user for input: stackoverflow.com/questions/23294658/… \$\endgroup\$ – Georgy Dec 15 '17 at 9:31
4
\$\begingroup\$

Try printing suffixes.index("Novemnonagintillion") and suffixes.index("Centillion"). You will get a value error. The list has these items, but there is no comma separating them. Also, because of this typo, len(suffixes) gives you 219 instead of the actual 220.

number = str("{:,}".format(number))

In the above statement, you do not need to make a call to str() because the format() does the job for you. So, it is just:

number = "{:,}".format(number)

There is a str.count() method, which you could use to get the number of commas in number.

# This replaces the while loop
number_of_commas = number.count(',')

Now, to get the number slice that is just before the first comma, you could use str.find(','), which gives you the lowest index in number where the comma is found. So, instead of splitting the number at the commas in it and making a list, you could just do number[:number.find(',')], which gives you the required number.

After inserting a comma between those two items in suffixes, I would write it like so:

number = "{:,}".format(int(input("Enter a number: "))) # Use comma as a thousands separator    

number_of_commas = number.count(',')

print(number[:number.find(',')], suffixes[number_of_commas])

Demo:

 Enter a number: 1234567890
 1 Billion

 >>> 

Hope that helps.

\$\endgroup\$
  • \$\begingroup\$ I took the liberty of correcting your curly quotes into straight quotes that python requires \$\endgroup\$ – Snowbody Dec 19 '17 at 18:14
2
\$\begingroup\$

To code what @Snowbody suggested in their second point:

suffixes = ["", "Thousand", "Million", "Billion", "Trillion", "Quadrillion","Quintillion", "Sextillion","Septillion","Octillion","Nonillion"]
beginnings = ["", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"]
endings_illion = ["dec", "vigint", "trigint", "quadrogint", "quinquagint", "sexagint", "septuagint", "octogint", "nonagint", "cent"]
endings_generic = ["primovegesimocent", "ducent", "trecent", "duotrigintatrecent", "quadringent", "quingent", "sescent", "septuagintisescent", "septingent", "octingent", "nongent"]
for ending in endings_illion:
    for beginning in beginnings:
        suffixes.append(beginning + ending + "illion")
for ending in endings_generic:
    for beginning in beginnings:
        suffixes.append(beginning + ending)

This has shorter lines, takes up less memory and is a lot easier to add to as you just add endings to endings_generic.

\$\endgroup\$
  • \$\begingroup\$ I think the endings_generic has a bug. "Quadringent" should come right after "trecent" right? \$\endgroup\$ – Snowbody Dec 19 '17 at 14:51
  • \$\begingroup\$ I'm not sure what is correct but that is how the OP does suffixes \$\endgroup\$ – 13ros27 Dec 19 '17 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.