Type Conversion
Python never silently coerces unrelated types together — you convert explicitly with functions like int(), float(), str(), and bool(). This topic covers how each conversion behaves, when it fails, and the difference between converting a value and parsing text.
Because Python is strongly typed, it will not implicitly combine incompatible types ("10" + 5 raises TypeError rather than guessing what you meant). Instead, you convert explicitly using built-in constructor functions: int(), float(), str(), bool(), and complex(). This topic covers what each conversion does, how it differs from parsing text, and where conversions can fail or truncate silently.
Why Python Requires Explicit Conversion
Python is strongly typed: it never silently coerces between unrelated types the way some languages implicitly convert strings and numbers in arithmetic. This is a deliberate design choice that prevents an entire class of subtle bugs where the wrong operation would silently "succeed" with an unexpected result.
try:
result = "10" + 5
except TypeError as e:
print(f"TypeError: {e}") # can only concatenate str (not "int") to str
# The fix is explicit conversion:
result = int("10") + 5
print(result) # 15int(): Converting to Integer
int(x) converts a float (by truncating toward zero, not rounding), a numeric string, or a bool to an integer. Given a string, it requires the string to represent a valid whole number in the specified base (decimal by default) — extra whitespace is tolerated, but a decimal point or other non-digit characters raise ValueError.
print(int(7.9)) # 7 -- truncates toward zero
print(int(-7.9)) # -7 -- also truncates toward zero, not -8
print(int("42")) # 42
print(int(" 42 ")) # 42 -- surrounding whitespace is stripped
print(int(True)) # 1
try:
int("3.14") # ValueError -- int() cannot parse a decimal point
except ValueError as e:
print(f"ValueError: {e}")The base Argument for Integer Parsing
When converting a string, int() accepts an optional second argument specifying the numeral base (2 through 36, or 0 to auto-detect a 0x/0o/0b prefix). This is distinct from converting a float or bool — the base argument is only valid when the first argument is a string.
print(int("1010", 2)) # 10 -- binary string parsed as base 2
print(int("ff", 16)) # 255 -- hex string parsed as base 16
print(int("0x1A", 0)) # 26 -- base 0 auto-detects the 0x prefix
print(int("17", 8)) # 15 -- octal parsing
try:
int("3.14", 10) # still ValueError -- base doesn't allow decimal points
except ValueError as e:
print(f"ValueError: {e}")float(): Converting to Floating Point
float(x) converts an int, a numeric string (including scientific notation and special values like "inf"/"nan"), or a bool to a float. Unlike int(), float() accepts strings with a decimal point or exponent.
print(float("3.14")) # 3.14
print(float("1e3")) # 1000.0
print(float(" 2.5 ")) # 2.5 -- whitespace stripped
print(float("inf")) # inf
print(float(42)) # 42.0
try:
float("abc") # ValueError
except ValueError as e:
print(f"ValueError: {e}")str(): Converting to String
str(x) produces the human-readable string representation of virtually any object by calling its __str__ method (falling back to __repr__ if __str__ isn't defined). Unlike int() and float(), str() essentially never fails — every object can be turned into some string.
print(str(42)) # '42'
print(str(3.14)) # '3.14'
print(str([1, 2, 3])) # '[1, 2, 3]'
print(str(None)) # 'None'
print(str(True)) # 'True'bool(): Converting to Boolean
bool(x) converts any object to True or False following the truth-value-testing rules covered in the Boolean Values topic (falsy: False, None, 0, 0.0, 0j, "", and empty containers; everything else truthy).
bool("0") being True is a frequent surprise: it's a non-empty string, so its truthiness has nothing to do with the character it contains.
print(bool(0)) # False
print(bool("0")) # True -- non-empty string, even though its content looks falsy!
print(bool([])) # False
print(bool([0])) # True -- non-empty list, even though its only element is falsycomplex(): Converting to Complex
complex(x) or complex(real, imag) converts numbers (or parses a string) into a complex number.
print(complex(5)) # (5+0j)
print(complex(2, 3)) # (2+3j)
print(complex("4+5j")) # (4+5j) -- note: no spaces allowed around + in the string formParsing vs Conversion
It's worth distinguishing two related but different ideas. Conversion changes an object from one type to a semantically equivalent object of another type (e.g. int(3.9) -> 3, a genuine truncation of a numeric value). Parsing interprets a string's textual content as structured data (e.g. int("42") reads the characters '4' and '2' and produces the number 42). Both use the same constructor functions in Python, but conceptually parsing is about interpreting text, while conversion is about changing an already-numeric type.
# Conversion: numeric type -> numeric type
print(int(3.9)) # 3 -- truncates a float value
# Parsing: text -> numeric type
print(int("3")) # 3 -- reads the digit character
# Parsing failure is common with malformed user input:
user_input = "abc"
try:
age = int(user_input)
except ValueError:
print("invalid input -- not a valid integer string")Invalid Conversions and Truncation Behavior
Two distinct failure modes matter in practice. An invalid conversion (text that doesn't represent a valid number in the target type) raises ValueError. A completely incompatible type combination in an operation (not a conversion call at all) raises TypeError. Separately, truncation is not an error at all — int() on a float always discards the fractional part silently, rounding toward zero, which is easy to mistake for rounding.
try:
int("twelve") # ValueError: invalid literal for int()
except ValueError as e:
print(type(e).__name__, e)
try:
int(None) # TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
except TypeError as e:
print(type(e).__name__, e)
print(int(9.99)) # 9 -- silent truncation, NOT rounding; no exception raisedKey Takeaways
Python never implicitly converts between unrelated types; use int(), float(), str(), bool(), or complex() explicitly.
int() truncates floats toward zero (never rounds) and parses strings strictly (no decimal points allowed, whitespace tolerated).
The base argument to int() only applies when parsing a string, letting you read binary, octal, hex, or arbitrary-base text.
str() almost never fails; int()/float() raise ValueError on malformed strings and TypeError on incompatible types.
bool("0") is True — string truthiness depends only on emptiness, never on the text's content.
Conversion changes an already-numeric type; parsing interprets text as structured data — both use the same constructor functions in Python.
age = int(input("Age: ")) # crashes with ValueError on any non-numeric input raw = input("Age: ") try: age = int(raw) except ValueError: print("Please enter a valid whole number.") age = None int() on unparseable text raises ValueError; user input should always be validated with a try/except (or a validation check) rather than assumed to be well-formed.
rounded_price = int(price * 100) / 100 # believes int() rounds -- it truncates rounded_price = round(price, 2) int() always truncates toward zero; it is not a rounding function. Use the built-in round() to round to a given number of decimal places.
Why does "10" + 5 raise TypeError in Python instead of producing 15 or "105"?
Python is strongly typed and never silently coerces between unrelated types like str and int; you must convert explicitly, e.g. int("10") + 5 or "10" + str(5).
What does int(-4.7) evaluate to?
-4 -- int() truncates toward zero, it does not round, so the fractional .7 is simply discarded regardless of sign.
How do you parse the hex string "ff" into the integer 255?
int("ff", 16) -- pass the base argument matching the string's numeral system.
Why does bool("0") evaluate to True?
Because "0" is a non-empty string; string truthiness depends only on whether the string is empty, not on its textual content.
What is the difference between converting and parsing, using int(3.9) and int("3") as examples?
int(3.9) converts an already-numeric float value by truncation; int("3") parses the characters of a string to produce a numeric value from text -- conceptually different operations that happen to share the same function.
What exception does int("3.14") raise, and why?
ValueError -- int() parsing a string requires the string to represent a whole number with no decimal point; "3.14" is not a valid integer literal string.
What exception does int(None) raise, and how does it differ from int("abc")?
int(None) raises TypeError because None is not a string, bytes-like object, or number at all; int("abc") raises ValueError because it IS a string, just one that doesn't represent a valid integer.
Write a function safe_int(text, default=0) that returns int(text) or the default value if conversion fails.
def safe_int(text, default=0): try: return int(text) except (ValueError, TypeError): return default print(safe_int("42")) # 42 print(safe_int("abc")) # 0 print(safe_int(None, -1)) # -1
Wrapping int() in a try/except that catches both ValueError (bad string) and TypeError (wrong type) lets the function fall back to a default instead of crashing.
What does this loop print for each value?
42, then "'3.14' is not a valid integer literal" (base 0 requires an integer-literal-style string, no decimal point), then 26 (0x1A auto-detected as hex), then "'abc' is not a valid integer literal".
Write code that converts a temperature given as a string like "98.6" to a float, then to the nearest int using round(), and prints both.
raw = "98.6" temp_f = float(raw) rounded = round(temp_f) print(temp_f, rounded) # 98.6 99 float() parses the decimal string into a float; round() (not int()) correctly rounds to the nearest whole number rather than truncating.
values = ["42", "3.14", "0x1A", "abc"]
for v in values:
try:
print(int(v, 0))
except ValueError:
print(f"{v!r} is not a valid integer literal")