Python type conversion in plain words: turn an int into a str, a str into a float, and back again, using int(), float(), str(), and bool(). Every path is shown with tested output and the catches that trip up beginners.
Here is the problem. A user, say a fellow named Anvay, types his age into your program and you get back the text "28", not the number 28. You try to check if "28" > 18 and Python throws an error in your face. Or you build a message with "Age: " + 28 and it crashes too. Python type conversion is how you fix all of this, and once it clicks you stop fighting the language and start working with it.
Think of it like travelling between countries with different money. Your phone number works everywhere, but your cash does not. Before you can spend rupees in Japan you have to change them into yen at a counter. Python is the same. A value in the wrong type is money in the wrong currency, and the conversion functions are the exchange counter. Some exchanges are clean and lossless. Some quietly shave a bit off (a rounding fee). And some counters slam the window shut if you hand them something they cannot read.
“Explicit is better than implicit.”
Tim Peters, The Zen of Python (PEP 20)
Last Updated: July 2026 | Tested on: Python 3.14.6 | Difficulty: Beginner | Reading Time: 18 minutes
Python type conversion happens in two ways. Sometimes the language converts automatically behind your back (implicit conversion), and sometimes you have to ask for it by name with a function like int(), str(), or float() (explicit conversion). The catch is that a few conversions lose data. int(3.9) gives you 3, not 4, because it chops the decimal off instead of rounding. And int("hello") stops your program cold. Knowing which conversions are safe and which are dangerous will save you hours of debugging.
Table of Contents
Conversion Map
Before we convert anything, here is the whole map in one picture: which conversions are safe, which lose data, and which can crash your program.
Read it like a metro map. The solid arrows between the coloured boxes are the safe rides: int to float, bool to int, anything to str. The arrow labelled int(3.14) is the lossy one, since it truncates and you lose the decimal. The dotted red arrow into ValueError is the dead end: hand int() or float() a string that is not a valid number and your program stops with an error.
Implicit Conversion (Coercion)
In some situations Python changes the type for you without being asked. You never call a function, it just happens. This is called coercion, and it only moves one way: from a “smaller” type up to a “bigger” one. Picture a ladder with bool at the bottom, then int, then float, then complex at the top. Python is happy to climb up that ladder on its own, but it never climbs down for you, and it never hops across to strings.
📄 implicit_conversion.py: Python converts automatically when it is safe
# int + float gives a float (the int gets promoted)
result = 10 + 3.14
print(f"10 + 3.14 = {result}, type: {type(result).__name__}")
# bool + int gives an int (True counts as 1, False as 0)
total = True + 7
print(f"True + 7 = {total}, type: {type(total).__name__}")
# bool + float gives a float
value = False + 2.5
print(f"False + 2.5 = {value}, type: {type(value).__name__}")
# Python does NOT implicitly convert str + int
# print("Age: " + 28) # this would be a TypeError
print("Age: " + str(28)) # so you convert it yourself
▶ Output
10 + 3.14 = 13.14, type: float True + 7 = 8, type: int False + 2.5 = 2.5, type: float Age: 28
What happened here: The rule is simple. When you mix numeric types in one expression, Python promotes the smaller one to the bigger one along that ladder: bool to int to float to complex. That is why 10 + 3.14 comes back as a float and True + 7 comes back as a plain int. But notice the last line. Python never quietly mixes strings and numbers. Writing "Age: " + 28 does not give you "Age: 28", it gives you a TypeError.
The Zen of Python says “Explicit is better than implicit,” and here Python takes its own advice: if joining a string and a number could mean two different things, it refuses to guess and makes you say what you want.
Explicit Conversion with int()
int() works like a cashier who only counts full notes. Hand over 3.9 and they report 3, because the loose change after the decimal point simply gets ignored, never rounded up. Keep that picture in mind while you read the examples, because “it ignores the change” explains almost every surprise this function will ever give you.
📄 int_conversion.py: turning values into integers
# float to int: TRUNCATES, it does NOT round
print(f"int(3.9) = {int(3.9)}") # 3, not 4
print(f"int(3.1) = {int(3.1)}") # 3
print(f"int(-3.9) = {int(-3.9)}") # -3 (chops toward zero)
# string to int: works only if the string is a whole number
print(f"int('42') = {int('42')}")
print(f"int('-7') = {int('-7')}")
# string with a base: parse binary, octal, hex
print(f"int('1010', 2) = {int('1010', 2)}") # binary 1010 is 10
print(f"int('FF', 16) = {int('FF', 16)}") # hex FF is 255
# bool to int
print(f"int(True) = {int(True)}") # 1
print(f"int(False) = {int(False)}") # 0
# These three all CRASH with ValueError:
# int("3.14") has a decimal point
# int("hello") is not a number
# int("") is an empty string
▶ Output
int(3.9) = 3
int(3.1) = 3
int(-3.9) = -3
int('42') = 42
int('-7') = -7
int('1010', 2) = 10
int('FF', 16) = 255
int(True) = 1
int(False) = 0
What happened here: int() truncates a float toward zero. It does not round, it just deletes whatever comes after the decimal point. That is why int(3.9) is 3 and int(-3.9) is -3. If you actually wanted rounding, round(3.9) would give you 4 instead. With strings, int() is picky: it only accepts a clean whole number, so int("42") works but int("3.14") crashes because of that decimal point. One genuinely handy trick is the two argument form. int("FF", 16) reads the string as base 16, which is perfect for parsing hex, binary, or octal text you got from a file or an API (Application Programming Interface).
Explicit Conversion with float()
If int() is the strict cashier, float() is a parking meter that takes both coins and notes. Whole numbers, decimals, even a decimal string like "3.14", all of them go in without a fuss.
📄 float_conversion.py: turning values into floats
# int to float: always safe
print(f"float(42) = {float(42)}")
# string to float: works with decimal strings
print(f"float('3.14') = {float('3.14')}")
print(f"float('-0.5') = {float('-0.5')}")
print(f"float('42') = {float('42')}") # works even without a decimal
# Special string values
print(f"float('inf') = {float('inf')}")
print(f"float('-inf') = {float('-inf')}")
print(f"float('nan') = {float('nan')}")
# bool to float
print(f"float(True) = {float(True)}")
print(f"float(False) = {float(False)}")
▶ Output
float(42) = 42.0
float('3.14') = 3.14
float('-0.5') = -0.5
float('42') = 42.0
float('inf') = inf
float('-inf') = -inf
float('nan') = nan
float(True) = 1.0
float(False) = 0.0
What happened here: float() is the easygoing cousin of int(). It happily reads both "42" and "3.14", so a stray decimal point does not bother it. It even understands three special strings: "inf" for infinity, "-inf" for negative infinity, and "nan" for “not a number” (you meet these when you do math that has no real answer). Going from an int up to a float is always safe, as long as the number sits inside float’s precision range. The reverse trip, float down to int, is the one that costs you, because it always truncates.
Explicit Conversion with str()
str() is like a label printer in a shop: no matter what you place on the counter, a book, a bag of apples, an empty box, the printer can always spit out a sticker describing it. Every Python value has a text form, so this is the one conversion with no failure cases. Say a user named Anvi has just signed up on your site and you need to show her profile line, mixing her name (already text) with her age (a number). Here is how that plays out.
📄 str_conversion.py: anything at all can become a string
# str() NEVER fails, every object has a string form
print(f"str(42) = '{str(42)}'")
print(f"str(3.14) = '{str(3.14)}'")
print(f"str(True) = '{str(True)}'")
print(f"str(None) = '{str(None)}'")
print(f"str([1, 2]) = '{str([1, 2])}'")
# Common job: building a message out of mixed values
name = "Anvi"
age = 25
# Way 1: glue strings together with str()
message = "Name: " + name + ", Age: " + str(age)
print(message)
# Way 2: an f-string (preferred, no str() needed)
message = f"Name: {name}, Age: {age}"
print(message)
▶ Output
str(42) = '42' str(3.14) = '3.14' str(True) = 'True' str(None) = 'None' str([1, 2]) = '[1, 2]' Name: Anvi, Age: 25 Name: Anvi, Age: 25
What happened here: str() is the one conversion that can never let you down. It never fails, because every Python object knows how to describe itself as text, even a list or None. In real code, though, you rarely call str() by hand. An f-string does it for you: anything you drop inside the {} braces gets turned into a string automatically, so f"Name: {name}, Age: {age}" just works. Reach for f-strings when you build messages. They are cleaner, faster, and far easier to read than gluing pieces together with + and str().
Explicit Conversion with bool()
bool() asks exactly one question, the same one you ask when you shake a tiffin box: is there anything inside? An empty box is False, a box with even one grain of rice in it is True. It never opens the box to judge what the contents are. So a name like "Aditi" (a person named Aditi in a users list, say) is True simply because the string is not empty.
📄 bool_conversion.py: the truthy and falsy rules
# Falsy values: everything that turns into False
print("--- Falsy values ---")
falsy_values = [0, 0.0, 0j, "", [], {}, set(), None, False]
for val in falsy_values:
print(f"bool({str(val):>8}) = {bool(val)}")
# Truthy: literally everything else
print("\n--- Truthy values ---")
truthy_values = [1, -1, 3.14, "Aditi", [0], {"key": "val"}, True]
for val in truthy_values:
print(f"bool({str(val):>15}) = {bool(val)}")
▶ Output
--- Falsy values ---
bool( 0) = False
bool( 0.0) = False
bool( 0j) = False
bool( ) = False
bool( []) = False
bool( {}) = False
bool( set()) = False
bool( None) = False
bool( False) = False
--- Truthy values ---
bool( 1) = True
bool( -1) = True
bool( 3.14) = True
bool( Aditi) = True
bool( [0]) = True
bool( {'key': 'val'}) = True
bool( True) = True
What happened here: The falsy rule is short enough to memorise. Zero of any numeric flavour, every empty container, the empty string, None, and False itself are all falsy. Everything else is truthy. The line that surprises people is [0], which comes out truthy. Why? Because the list is not empty: it holds one item. Python only cares whether the container has anything in it, not whether the thing inside happens to be zero. That is exactly why if my_list: is the Pythonic way to ask “does this list have any items,” not “are the items truthy.”
The input() Trap
This is the number one beginner bug, and almost everyone hits it once. input() always hands you a string, even when the user clearly typed a number. So the value looks like a number on screen but behaves like text. Here is the trap, run line by line in the REPL (Read-Eval-Print Loop) so you can see the real error Python gives you.
🚫 The trap that catches every beginner (Python REPL)
>>> user_age = "28" # input() always gives you a string >>> user_age > 18 # you meant a number, but this is text vs number Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: '>' not supported between instances of 'str' and 'int'
Python refuses to compare a string with an integer, so it stops with a TypeError instead of guessing. And here is the sneakier version of the same bug. If you compare the string against another string, Python does not complain at all, it just compares them letter by letter like words in a dictionary, which gives you nonsense answers when the values are ages.
🚫 Even worse: string vs string compares like dictionary order (Python REPL)
>>> "28" > "18" # looks right by luck True >>> "9" > "18" # but a 9 year old is now "older" than an 18 year old True
That second result is the dangerous one. As text, "9" beats "18" because the character 9 comes after the character 1, exactly the way “zebra” sorts after “apple.” The comparison runs without any error, your code keeps going, and the bug hides until a real user breaks something. The fix is always the same: convert first, compare second.
✅ The fix: convert first, then compare
# Always convert input to the type you actually need
user_input = "28" # this is what input() would return
age = int(user_input) # convert to int FIRST
if age > 18:
print(f"Age {age}: Adult")
# Safer version that survives bad input
user_input = "not a number"
try:
age = int(user_input)
print(f"Your age is {age}")
except ValueError:
print(f"'{user_input}' is not a valid number")
▶ Output
Age 28: Adult 'not a number' is not a valid number
What happened here: input() returns a string, always, no exceptions. Type 28 and you get the text "28", never the number 28. So before you do any math or any comparison, convert it with int() or float(). The second half of the fix wraps that conversion in try and except ValueError so that a user typing “twenty eight” or leaving the box blank gets a friendly message instead of a crash. The try/except idea gets its own full post later in the exception handling tutorial. For now, just remember the order: convert first, then use the value.
Common Mistakes
Mistake 1: Thinking int() rounds
📄 rounding_trap.py
# int() TRUNCATES, it just chops off the decimal
print(f"int(3.9) = {int(3.9)}") # 3, NOT 4
print(f"int(-3.9) = {int(-3.9)}") # -3, NOT -4
# If you actually want rounding, use round()
print(f"round(3.9) = {round(3.9)}") # 4
print(f"round(3.5) = {round(3.5)}") # 4 (banker's rounding!)
print(f"round(4.5) = {round(4.5)}") # 4 (rounds to the even number!)
▶ Output
int(3.9) = 3 int(-3.9) = -3 round(3.9) = 4 round(3.5) = 4 round(4.5) = 4
Why: int() always truncates toward zero, full stop. round() is the one with a surprise: it uses banker’s rounding (round half to the nearest even number), which is why both round(3.5) and round(4.5) land on 4. This trips up people who expect 4.5 to go up to 5. If you genuinely want “always round up,” skip both and use math.ceil(3.1), which gives you 4.
Mistake 2: Converting a decimal string with int()
🚫 This crashes
# int("3.14") # ValueError: invalid literal for int() with base 10: '3.14'
✅ Two-step conversion
# Go string, then float, then int (two hops)
value = int(float("3.14"))
print(f"int(float('3.14')) = {value}") # 3
Why: int() reads "42" happily but chokes on "3.14", because it expects a clean whole number with no decimal point. So when a string might carry a decimal, take two hops: float() first to accept the decimal, then int() to chop it down to a whole number.
Mistake 3: String concatenation instead of conversion
🚫 Confusing output
# Two strings "5" + "3" get joined, not added a = "5" b = "3" print(a + b) # "53", string concatenation! print(int(a) + int(b)) # 8, real numeric addition
▶ Output
53 8
Why: The + operator wears two hats depending on what is on either side of it. Between strings it glues them together, between numbers it adds them. So "5" + "3" gives "53", while 5 + 3 gives 8. The values look identical on screen, which is exactly why this bug is so easy to miss. Convert to numbers first whenever you mean to do math.
Best Practices
- DO always convert
input()to the type you need:age = int(input("Age: ")) - DO use
try/except ValueErrorwhen converting user input - DO use f-strings instead of
str()concatenation:f"Age: {age}" - DO use
round()when you want rounding,int()when you want truncation - DON’T assume
int()rounds. It truncates toward zero. - DON’T pass a decimal string straight to
int(). Useint(float(s))instead. - DON’T expect Python to mix strings and numbers for you. It will not, so convert by hand.
Conclusion
Python type conversion runs on one clear idea: it converts for you only when nothing can be lost (like int up to float), and for everything else you have to ask out loud. The four functions int(), float(), str(), and bool() each carry their own small rules and catches, but the single mistake you will make most often is forgetting that input() always hands back a string. Burn the pattern into your memory: read the value, convert it, then use it.
You will reach for this on day one of real coding: reading a price from a CSV (Comma-Separated Values) file, parsing a port number from a config file, turning a form field into an integer in a web app. Every one of those arrives as text and becomes a number through one small Python type conversion.
Next up: Python Error Messages Explained: How to Read a Traceback, which picks up right where those ValueError and TypeError crashes left off and shows you how to read a traceback line by line so the message tells you exactly what to fix. And if you want the full path from these fundamentals all the way to AI and ML, browse every post in order at the Python + AI/ML tutorial series home.
Practice Exercises
- Exercise 1: Convert “42” to int, add 8, convert back to string, concatenate with ” is the answer”.
- Exercise 2: Write a safe float converter returning a default on failure.
- Exercise 3: Build a temperature converter handling int, float, and string inputs.
Frequently Asked Questions
What is the difference between implicit and explicit type conversion in Python?
Implicit conversion (coercion) happens automatically when Python promotes a smaller type to a larger one in expressions, like int + float producing a float. Explicit conversion requires calling functions like int(), str(), or float() to convert values yourself. Python only does implicit conversion when no data can be lost.
Does int() round or truncate in Python?
int() truncates toward zero. It chops off the decimal part without rounding. int(3.9) gives 3, not 4. int(-3.9) gives -3, not -4. If you want rounding, use round() instead. For always rounding up, use math.ceil().
Why does input() always return a string in Python?
input() reads raw text from the keyboard. Python has no way to know if the user intended to type a number, a name, or a date, so it returns everything as a string. You must convert it yourself: age = int(input('Age: ')). Always wrap conversions in try/except to handle invalid input gracefully.
How do I convert a string with a decimal point to an integer?
You cannot pass a decimal string directly to int(), because int('3.14') raises a ValueError. Use a two-step conversion: int(float('3.14')) which first converts to float 3.14, then truncates to integer 3.
What values are falsy in Python?
These values evaluate to False in boolean context: 0, 0.0, 0j, '' (empty string), [] (empty list), {} (empty dict), set() (empty set), None, and False itself. Everything else is truthy. Note that [0] is truthy because the list is not empty.
Can you convert any type to a string in Python?
Yes. str() never fails, because every Python object has a string representation. str(42) gives '42', str(None) gives 'None', str([1,2]) gives '[1, 2]'. In practice, use f-strings instead: f'value is {x}' automatically converts x to a string.
Interview Questions on Python Type Conversion
If you can walk through these without peeking, you are ready for this topic in an interview.
Q: You read a price column from a CSV file and float("1,299.00") crashes with a ValueError. What went wrong and how do you fix it?
float() understands digits, one decimal point, a sign, and nothing else, so the thousands separator comma makes the string invalid. Strip it first with value.replace(",", "") and then convert, which turns "1,299.00" into 1299.0. For data from multiple regions be careful, because some countries use the comma as the decimal mark, and then Python’s locale module is the proper tool.
Q: A signup check if age > "18" quietly lets a 9 year old through without any error. What do you check first?
Check the type of age with type(age). If both sides are strings, Python compares them character by character like dictionary words, and "9" > "18" is True because the character 9 sorts after 1. No error is raised, so the bug hides. The fix is to convert to int right where the data enters the program, then compare numbers with numbers.
Q: What does bool("False") return, and why?
It returns True. bool() only checks whether a string is empty, it never reads the content, so any non-empty string is truthy, including "False", "0", and " ". If you need to parse a text flag from a config file, compare it explicitly, for example value.lower() == "true".
Q: Why does True + True equal 2 in Python?
Because bool is a subclass of int: True is the integer 1 and False is 0, so arithmetic promotes them to plain ints. This is also behind a handy counting trick: sum(flag_list) tells you how many True values a list holds.
Q: When would you use the two argument form int("1010", 2)?
When the string holds a number written in a base other than 10. int("1010", 2) reads the text as binary and gives 10, and int("FF", 16) reads hex and gives 255. It shows up whenever you parse binary flags, hex colour codes, or memory addresses that arrive as text from a file or an API.
Q: Your program accepts a typed number that may be blank or contain junk. Is checking value.isdigit() before calling int(value) good enough?
No. isdigit() returns False for perfectly valid numbers like "-7" (the minus sign is not a digit) and "3.14", so you would reject good input. The robust beginner-friendly pattern is to just attempt the conversion inside try and catch ValueError, which handles blanks, junk, signs, and spaces in one place.
Further reading: for the full reference, see the official Python documentation.
Related Posts
Previous: Python: Data Types (int, float, str, bool, complex, bytes, bytearray, None)
Next: Python Error Messages Explained: How to Read a Traceback
Series Home: Python + AI/ML Tutorial Series

No comment