Built-in Types
Built-in Types
Every value at the desk is an object of some class. The boring default is to look with type, convert on purpose with int / str / float, and never hope Python will guess.
Mental model
A value is an object in memory. A name is a label stuck on that object. type(x) returns the class of whatever x currently points at. id(x) is an integer that identifies that object for the life of the process. Use id to see whether two names point at the same object, not whether they look equal.
Built-ins you will use every day:
| Type | What it is at the desk |
|---|---|
int |
counts, ticket ids, prices in cents |
float |
measurements (not money) |
bool |
True / False |
str |
labels, names, status words |
NoneType |
“not set” — one object, spelled None |
bool is a subclass of int: True behaves like 1 and False like 0 if you let it. Do not let it. Treat a bool as yes/no.
Conversion is a function call: int("12"), str(7), float("3.5"), bool(0). A failed conversion raises ValueError. That is better than silent garbage.
Worked examples
Case 1: Look at a ticket
Save as ticket_types.py. Print the type of each field so you can see what you actually stored.
# ticket_types.py
def main():
ticket = {
"id": 7,
"table": 12,
"paid": False,
"note": "window",
"closed_at": None,
}
for key in ticket:
value = ticket[key]
print(key, type(value), value)
if __name__ == "__main__":
main()Run:
uv run python ticket_types.pyOutput:
id <class 'int'> 7
table <class 'int'> 12
paid <class 'bool'> False
note <class 'str'> window
closed_at <class 'NoneType'> None
None is not the string "None" and not False. It is its own type, with one value.
Case 2: Convert on purpose
The kitchen sends table numbers as text. Prices arrive as strings too. Convert before you add.
# convert_order.py
def main():
table_text = "12"
cents_text = "1850"
table = int(table_text)
cents = int(cents_text)
print(type(table_text), type(table))
print(f"table {table}: {cents} cents")
print(str(cents) + "c")
print(float(cents) / 100)
if __name__ == "__main__":
main()Run:
uv run python convert_order.pyOutput:
<class 'str'> <class 'int'>
table 12: 1850 cents
1850c
18.5
int("12") is a new object. The original string is unchanged. str(cents) + "c" only works because both sides are strings — adding an int to a str raises TypeError.
Case 3: Equality is not identity
== asks “do these look the same?” id asks “are these the same object?” Two lists with the same contents are equal and still two objects.
# same_object.py
def main():
a = [7, 12]
b = a
c = [7, 12]
print("a == b", a == b)
print("a is b", a is b)
print("a == c", a == c)
print("a is c", a is c)
print("same id a,b", id(a) == id(b))
print("same id a,c", id(a) == id(c))
if __name__ == "__main__":
main()Run:
uv run python same_object.pyOutput:
a == b True
a is b True
a == c True
a is c False
same id a,b True
same id a,c False
b = a does not copy the list. It sticks a second name on the same list. id numbers themselves change every run; comparing them is the useful bit. Use is for None. Use == for values.
Case 4: bool is not a number you should add
# bool_is_int.py
def main():
paid = True
print(type(paid), paid)
print(paid == 1)
print(paid + 2)
print(True + True)
if __name__ == "__main__":
main()Run:
uv run python bool_is_int.pyOutput:
<class 'bool'> True
True
3
2
The program is legal. Adding True to a count is still a bug. Keep bools in if tests and in fields named like paid.
The trap
Python will convert in a few places without asking — if ticket["note"]: treats "" as false, and int(True) is 1. It will not convert "12" + 1. People then reach for implicit tricks (paid + 1) or for id as a substitute for ==.
Save as bad_add.py:
# bad_add.py
def main():
table = "12"
print(table + 1)
if __name__ == "__main__":
main()Run:
uv run python bad_add.pyOutput (the process exits non-zero):
Traceback (most recent call last):
File "bad_add.py", line 8, in <module>
main()
File "bad_add.py", line 4, in main
print(table + 1)
TypeError: can only concatenate str (not "int") to str
The fix is int(table) + 1 or table + str(1), chosen on purpose. Read the traceback from the bottom.
The boring rule
- Inspect with
typewhen a value surprises you. - Convert with
int,str,float,boolat the boundary (input, file, kitchen ticket). - Store money as
intcents. Usefloatfor measurements, not for prices. - Compare values with
==. Compare toNonewithis. - Do not add bools. Do not use
idas a hash you print in logs.
Try this
- In
convert_order.py, feedcents_text = "18.50"and catchValueError. Print a clear message. Then parse it withfloatand convert to cents withround. - In
ticket_types.py, add acoversfield as the string"4". Convert it tointbefore you print. - In
same_object.py, append99toband printaandc. Notice which list changed.