Converting numbers to words - Python

I'm new to programming. I tried to find a way to convert numbers into letters with Python. I would like to receive some advice to improve myself. This program works for numbers between 1 and 106 and I would like to know how the logic of the program can be improved.

def changeNumberIntoLetter(value):
number=numToLetter(value)
return number

def numToLetter(value): #The function converts the numbers into letters.
if value==1: return 'one'
elif value==2: return 'two'
elif value==3: return 'three'
elif value==4: return 'four'
elif value==5: return 'five'
elif value==6: return 'six'
elif value==7: return 'seven'
elif value==8: return 'eight'
elif value==9: return 'nine'
elif value==10: return 'ten'
elif value==11: return 'eleven'
elif value==12: return 'twelve'
elif value==13: return 'thirteen'
elif 13<value<=19: return composeTeen(value)
elif value>19:
if value==20: return 'twenty'
elif value==30: return 'thirty'
elif value==50: return 'fifty'
elif value==10**2: return 'one hundred'
elif value==10**3: return 'one thousand'
elif value==10**5: return 'one hundred thousand'
elif value==10**6: return 'one milion'
elif value>=20: return composeNumbers(value)
else: exit('Out of range')
else: return ''

def composeNumbers(value):   #The function build every number biger than 40
if 40<=value<10**2:
value1=int(str(value)[0])
value2= int(str(value)[1])
if value1==2:
value1= 'twen'
return value1 + 'ty' + '-' + numToLetter(value2)
if value1==3:
value1='thir'
return value1 + 'ty' + '-' + numToLetter(value2)
if value1==8:
value1='eigh'
return value1 + 'ty' + '-' + numToLetter(value2)
elif value1==5:
value1='fif'
return value1 + 'ty' + '-' + numToLetter(value2)
return numToLetter(value1) + 'ty' + '-' + numToLetter(value2)
elif 10**2<=value<10**3:
value1=int(str(value)[0])
value2= int(str(value)[1:])
return numToLetter(value1) + ' ' + 'hundred' + ' ' + numToLetter(value2)
elif 10**3<=value<10**4:
value1=int(str(value)[0])
value2=int(str(value)[1:])
elif 10**4<=value<10**5:
value1=int(str(value)[0:2])
value2=int(str(value)[2:])
elif 10**5<=value<10**6:
value1=int(str(value)[0:3])
value2=int(str(value)[3:])
return numToLetter(value1) + ' ' + 'thousand' + ' ' + numToLetter(value2)

def composeTeen(value): #The function takes the unit and then converts it into letter to build the word.
value= int(str(value)[-1])  #It turns elem in string to take the last position and it converts it again in integer to change it in letters. Then it composes the word adding 'teen' at the end.
value= numToLetter(value)
if value=='five': value= 'fif'
value= value + 'teen'
return value

• Something which could help you make this code effective is if you have a look at this code, codereview.stackexchange.com/questions/182833/…, it is not an answer because it doesn't do it just words but it uses techniques which could be good for what you are trying to do Commented Feb 22, 2019 at 17:59

This is good work! Well done as a beginner programmer.

def changeNumberIntoLetter(value):
number=numToLetter(value)
return number


This function doesn't really do anything useful to be honest. Can't you directly use numToLetter.

if value==1: return 'one'
elif value==2: return 'two'
elif value==3: return 'three'
elif value==4: return 'four'
elif value==5: return 'five'
elif value==6: return 'six'


Instead of creating lot of if statements. Try using a dictionary like this:

NUM_TO_WORD_MAPPING = {1: "one", 2: "two"}


and you can refer the string to number using

if value in NUM_TO_WORD_MAPPING:
return NUM_TO_WORD_MAPPING[value]
else:
# ... special cases.


As sergiy-kolodyazhnyy said, you can also use a list or a tuple.

int(str(value)[-1])


use value % 10 to extract numbers easily. This is modulo operator and returns remainder after division.

Bonus Content: Code Style

How your code looks like is as equally important as how you implement it. Because you and other programmers will spend time reading it.

“Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.”

― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Let's take a look at your function

def numToLetter(value): #The function converts the numbers into letters.


I recommend that you use snake_case (number_to_letter) as it is considered industry standard for python programs. Comments are better done using """ comment """ style.

In this scenario however comment is not really required because your function name says that it's purpose is converting number to letter. People usually forget to update comments and it ends up giving incorrect information. If you want to use documentation comments anyway, make sure that comments are always up to date.

• Thank you so much! This is really helpful; I'll improve the code with these changes Commented Feb 22, 2019 at 20:17
• A list or tuple could probably also work, with 0 item being None. A number itself would correspond to index position of the word item in list/tuple Commented Feb 22, 2019 at 20:34
• @RobertaBelladonna, don't forget to accept this answer if you think it answered your question Commented Feb 22, 2019 at 22:30
• Updated with more info. @SergiyKolodyazhnyy yes you are correct. :) Commented Feb 23, 2019 at 16:31

I did this by inflect pypi library:

import inflect

ig = inflect.engine()
print(ig.number_to_words(85))


Out:

eighty-five


[NOTE]:

Install inflect library by pip:

\$ pip install inflect


Furthermore, I found this method too:

>>> import num2word
>>> num2word.to_card(15)
'fifteen'
>>> num2word.to_card(55)
'fifty-five'
>>> num2word.to_card(1555)
'one thousand, five hundred and fifty-five'


[NOTE]:

These are the Github link of mentioned libraries if you want to know how they implemented:

• Offloading to others is good, Pythonic even. But this doesn't really help the person improve their own programing abilities. Commented Feb 22, 2019 at 18:26
• I got it, so if he wants to know how these libraries implemented, I'll update my answer with the Github links of these libraries. Commented Feb 22, 2019 at 18:34
• @Peilonrayz - True, good point. I can see it both ways. While it doesn't help OP program the code themselves, it hopefully it shows OP that in Python, there's likely a library already out there to help you so you don't reinvent the wheel (no pun intended!). Commented Feb 22, 2019 at 19:46

Process

I'm relatively new to Python, so this may not be the Pythonic approach, but I think it's useful to use test-driven development for problems like this. Spelling out numbers has lots of special cases, which can be overwhelming, so working incrementally can be one way to get a handle on it.

I started by writing:

def main():
for i in range(0, 20):
print(f"{i}: {spellNumber(i)}")


This isn't literally a test, because it doesn't check the result, but it shows me the output for the first 20 integers, which is where many of the special cases are.

Then I wrote a simple implementation that uses an array to solve it:

def spellNumber(n):
units = ["zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine", "ten", "eleven",
"twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen"]
if n < 20: return units[n]
return "out of range"


Once that appeared to be working, I extended my "test" by sampling values up to 100:

  for i in range(20, 100, 12):
print(f"{i}: {spellNumber(i)}")


Note that I kept the original part of the test, so that I make sure I don't break the part that was already working when I try to extend it.

As I modified spellNumber to handle each new range of inputs, I sometimes adjusted the code I'd already written to make it easier for the new code to use it. Having all the tests helped me make sure I didn't break the older code in the process.

Along the way, I made mistakes, and had to figure out how to fix outputs like "thirty-zero" in a way that still allowed for "zero."

Style

Your comments on what the functions do are useful, but I think the Pythonic way to document a function is to use a docstring. So, instead of:

def myFunction(): # This is what my function does


Write:

def myFunction():
"""This is what my function does."""


It's appropriate to use regular comments for details in your code, but comments that describe how a function (or class or file) is intended to be used should probably be docstrings.

Whenever you have a lot of similarly structured if-statements (if this: blah elif that: blub elif other: ...), consider whether using a data structure would be a better way to represent the problem.