Type Conversion (Casting) in Python
A practical guide to converting between Python types — int, float, str, bool, list, tuple, set, dict — when conversions fail, and the common pitfalls to avoid.
What you'll learn
- ✓The difference between implicit and explicit type conversion
- ✓How to convert between numbers, strings, and booleans
- ✓How to convert between the collection types
- ✓When and why conversions raise errors
- ✓The truthiness rules Python uses for bool() conversion
Prerequisites
- •A working knowledge of Python data types — see Data Types
In real programs, values rarely arrive in the type you need. User input is a string, even when it represents a number. JSON arrays become Python lists when you want a set. CSV rows are strings of strings. Type conversion — also called casting — is the bridge between data as you receive it and data as you can compute with it.
This post is the last in the data-types section of the series. It covers every conversion you will use in practice and the errors you will see when a conversion isn’t possible.
Implicit vs explicit conversion
Python performs a small amount of implicit conversion between numeric types — adding an int and a float produces a float:
result = 3 + 2.5
print(result) # 5.5
print(type(result)) # <class 'float'>
This is the only conversion Python does automatically. Everything else must be explicit, using the built-in constructors: int(), float(), str(), bool(), list(), tuple(), set(), dict().
Explicit conversion is a deliberate choice. If you forget it, Python raises a TypeError rather than silently doing the wrong thing:
print("Age: " + 30)
# TypeError: can only concatenate str (not "int") to str
print("Age: " + str(30)) # "Age: 30"
Numeric conversions
To int
print(int(3.9)) # 3 — truncates toward zero
print(int(-3.9)) # -3
print(int("42")) # 42
print(int(" 42 ")) # 42 — whitespace is tolerated
print(int(True)) # 1
print(int(False)) # 0
int() accepts a second argument: the base to parse a string in.
print(int("ff", 16)) # 255
print(int("1010", 2)) # 10
Conversions that don’t make sense raise ValueError:
int("3.9") # ValueError — int() does not parse decimals from strings
int("hello") # ValueError
To convert a numeric string with a decimal, parse it as a float first:
int(float("3.9")) # 3
To float
print(float(3)) # 3.0
print(float("3.14")) # 3.14
print(float("1e6")) # 1000000.0
print(float(True)) # 1.0
Special values are accepted:
print(float("inf")) # inf
print(float("nan")) # nan
To bool
bool() follows Python’s truthiness rules, which apply broadly across the language:
# Falsy values
bool(0) # False
bool(0.0) # False
bool("") # False
bool([]) # False
bool({}) # False
bool(set()) # False
bool(None) # False
# Everything else is truthy
bool(1) # True
bool(-7) # True
bool("False") # True — non-empty string is truthy
bool([0]) # True — non-empty list is truthy
This catches beginners constantly: bool("False") is True because the value being checked is a non-empty string, not the boolean.
To convert a string like "True" or "False" to a real boolean, do it explicitly:
def parse_bool(s: str) -> bool:
return s.strip().lower() in ("true", "1", "yes", "y")
print(parse_bool("True")) # True
print(parse_bool("False")) # False
String conversion
str() returns the most readable representation of any value:
print(str(42)) # "42"
print(str(3.14)) # "3.14"
print(str(True)) # "True"
print(str([1, 2, 3])) # "[1, 2, 3]"
print(str(None)) # "None"
For inserting values into messages, prefer f-strings over manual conversion — they handle the call to str() for you and are easier to read:
name = "Alice"
age = 30
print(f"{name} is {age}") # better than: print(str(name) + " is " + str(age))
Two related functions appear in real code: repr() returns a more developer-oriented representation (often quoted), and format() accepts the same specifier syntax as f-strings.
print(repr("hello")) # 'hello' — useful for debugging
print(format(0.5, ".1%")) # '50.0%'
Collection conversions
The four collection constructors — list(), tuple(), set(), dict() — convert from any compatible iterable.
Between sequences
print(list((1, 2, 3))) # [1, 2, 3]
print(tuple([1, 2, 3])) # (1, 2, 3)
print(list("hello")) # ['h', 'e', 'l', 'l', 'o']
print(tuple(range(3))) # (0, 1, 2)
To set (deduplicates)
print(set([1, 2, 2, 3, 3, 3])) # {1, 2, 3}
print(set("hello")) # {'h', 'e', 'l', 'o'}
This is the canonical way to deduplicate a list — though it loses order. For order-preserving deduplication, use list(dict.fromkeys(items)).
To dict
dict() accepts an iterable of (key, value) pairs:
pairs = [("a", 1), ("b", 2)]
print(dict(pairs)) # {'a': 1, 'b': 2}
# zip() is the natural companion
names = ["alice", "bob"]
ages = [30, 25]
print(dict(zip(names, ages))) # {'alice': 30, 'bob': 25}
The reverse direction:
d = {"a": 1, "b": 2}
print(list(d.keys())) # ['a', 'b']
print(list(d.values())) # [1, 2]
print(list(d.items())) # [('a', 1), ('b', 2)]
Try it yourself. Given a list of names with duplicates, write a one-liner that returns the same list with duplicates removed and order preserved. Then write a second one-liner that returns just the unique names as a set.
Reading numbers from user input
input() always returns a string. Forgetting this is the single most common beginner mistake involving conversion:
age = input("Your age: ")
print(age + 5)
# TypeError: can only concatenate str (not "int") to str
The fix is to convert explicitly:
age = int(input("Your age: "))
print(age + 5)
For robustness against bad input, wrap the conversion in a try/except (we will cover error handling in detail later in the series):
raw = input("Your age: ")
try:
age = int(raw)
except ValueError:
print("Please enter a whole number.")
When conversions fail
Each constructor raises a specific error for inputs it can’t handle. Knowing them on sight saves time:
| Constructor | Common failure | Error |
|---|---|---|
int("hi") | Unparseable string | ValueError |
int(3.14, 10) | base only valid for strings | TypeError |
float("two") | Unparseable string | ValueError |
dict([1, 2, 3]) | Items aren’t pairs | TypeError |
list(5) | Argument is not iterable | TypeError |
A useful mental model: ValueError means “the type is right but the value is wrong” — int("hi") is a string, but not one representing an integer. TypeError means “the type itself is wrong for this operation.”
The isinstance trick
Sometimes you don’t want to convert blindly — you want to act differently depending on what you receive. The idiomatic check is isinstance:
def to_list(value):
if isinstance(value, list):
return value
if isinstance(value, (tuple, set)):
return list(value)
return [value]
print(to_list([1, 2])) # [1, 2]
print(to_list((1, 2))) # [1, 2]
print(to_list("hi")) # ['hi'] — note: strings aren't iterated into chars
This pattern appears in many APIs to accept “one or many” inputs gracefully.
Try it yourself. Write a small function that accepts either a string of comma-separated numbers ("1,2,3") or a list of numeric strings (["1", "2", "3"]) and returns the sum as an integer. Use isinstance, split, and int. Confirm both inputs give the same result.
Recap
You now know:
- Python converts numeric types implicitly; everything else is explicit
int,float,str,boolhandle the scalar conversions, each with predictable failure modes- Truthiness drives
bool()— empty things areFalse, everything else isTrue list,tuple,set,dictconvert between collection types and deduplicate when relevantinput()always returns a string — convert explicitly when you need a numberValueErrormeans the value can’t be interpreted;TypeErrormeans the type is wrong
Where to go next
You have now completed the data types section of the Python series. You can write programs that store and process numbers, text, and every important collection type Python provides. Most beginner Python courses spend twice this many words covering the same ground less precisely.
Upcoming posts will cover:
- Conditional logic —
if,elif,else, and the truthiness rules in context - Loops —
for,while,break,continue, and patterns that replace many loops - Functions — defining, calling, default arguments, keyword arguments
- Error handling —
try/except/else/finally - Object-oriented Python — classes, methods, inheritance
Each will follow the same format you’ve come to expect.
Questions or feedback? Email codeloomdevv@gmail.com.