Python's claim that everything is an object is literal and absolute. Every value in the language, from a simple integer to a complex framework class, is a full object with identity, type, and attributes you can inspect at runtime. An integer has methods you can call, a function has attributes like a name and a docstring, and a class is something you can modify at runtime and pass around as an argument.
Even the type object that describes what kind of thing something is, is itself an object in memory. The article on the Python object model introduced the three core properties of identity, type, and value. This article proves the claim by walking through different categories of things in Python and showing that each one behaves like a full object.
You can verify this yourself with three built-in functions: id returns the unique identity, type returns the type object, and dir returns the list of attributes. Every example in this article uses one or more of these to confirm the value in question is a real object.
Numbers are objects
The simplest values in Python are numbers, and even these are full objects with methods and attributes. An integer literal has a type, a unique identity, and a full set of methods you can call on it.
x = 42
print(type(x))
print(id(x))
print(x.bit_length())The integer 42 has a type of int and a unique memory address. Its bit_length method returns the number of bits needed to represent it in binary.
<class 'int'>
4371883088
6You can call any method on an integer literal by wrapping it in parentheses. This avoids the parser treating the dot as a decimal point.
print((42).bit_length())
print((42).__add__(8))
print(dir(42)[:5])The add method is what Python calls internally when you write 42 + 8. The dir output shows the first five of dozens of attributes, confirming this is a real object with an extensive interface.
6
50
['__abs__', '__add__', '__and__', '__bool__', '__ceil__']Every built-in type follows the same pattern. A string has methods like upper and split, and a float has methods like as_integer_ratio. There are no lightweight primitive types that cannot be inspected.
Functions are objects
A function is not just a block of code. It is an object you can assign to variables, store in data structures, pass as an argument, and return from other functions. Every function has a type, identity, and attributes.
def greet(name):
return f"Hello, {name}"
print(type(greet))
print(id(greet))
print(greet.__name__)The function has type function, a unique identity, and a name attribute that stores the function's original name. This is what lets tools like decorators and debuggers introspect a function the same way they would any other object.
<class 'function'>
4373542208
greetYou can assign the function to another name and call it through that new name. Both names point to the same function object.
say_hello = greet
print(say_hello("Maya"))Calling say_hello runs the same function object that greet refers to. This works because assignment never copies a function, it just creates another name pointing at the identical object in memory.
Hello, MayaThis is what makes higher-order functions possible. You can pass a function object to another function like map, filter, or sorted with a key argument. The article on higher-order functions in Python covers this pattern in detail.
You can also store function objects in a list and iterate over them. Each element is a reference to a function object, no different from a list of integers.
funcs = [greet, len, str, print]
for f in funcs:
print(f.__name__)Each function in the list is a full object with its own name attribute. Storing callables this way is how dispatch tables and plugin systems map a string or key to the function that should run.
greet
len
str
printClasses are objects
In many languages, a class is a static blueprint that exists only at compile time. In Python, a class is an object created at runtime. You can inspect it, modify it, pass it around, and even create it dynamically.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
print(type(Point))
print(id(Point))
print(Point.__name__)The class Point is an instance of type with its own identity and name attribute. Because a class is an object like any other, you can attach it to variables, pass it around, and inspect it before ever creating an instance from it.
<class 'type'>
4373881792
PointBecause a class is an object, you can pass it to a function. That function can then create instances from the class argument without knowing the class name in advance.
def create_instance(cls, *args):
return cls(*args)
p = create_instance(Point, 3, 4)
print(p.x, p.y)The create_instance function accepts any class and any constructor arguments, then returns a new instance. This pattern only works because the class itself is a value you can pass around, not a special construct the compiler resolves ahead of time.
3 4This is the foundation of factory patterns and many metaprogramming techniques, since a class being a first-class object is what lets you manipulate it like any other value.
Modules are objects
When you import a module, Python creates a module object that holds all the names defined in that module. You can inspect a module the same way you inspect any other object.
import math
print(type(math))
print(id(math))
print(dir(math)[:5])The math module is an object with type module. Its dir output shows the first few of many attributes it carries.
<class 'module'>
4373864192
['__doc__', '__file__', '__loader__', '__name__', '__package__']Module attributes include every function and constant defined in that module. When you write math.sqrt(16), you are accessing the sqrt attribute of the math module object, which holds a function object.
You can assign a module to a different name and use it through that name. Both names point to the same module object in memory.
m = math
print(m.pi)
print(m.sqrt(25))The import statement binds the module object to a name. Reassigning that name just creates another label for the same object.
3.141592653589793
5.0Types are objects
The type objects that describe what other objects are, are themselves objects. The int class returned by type(42) has its own type, which is type.
print(type(int))
print(type(str))
print(type(list))
print(type(type))Every built-in type is an instance of type. And type itself is also an instance of type, which closes the metaclass loop.
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>Every type inherits from object. You can verify this with isinstance checks, and this single root is what lets generic code written for object apply uniformly across every built-in and user-defined type.
print(isinstance(int, object))
print(isinstance(type, object))
print(int.__bases__)The integer type's base class is object, and type itself is also an instance of object. This is what closes the loop at the top of Python's type hierarchy, since even the ultimate base class is an object of type type.
True
True
(<class 'object'>,)This is why isinstance(42, object) returns True. The integer 42 has type int, and int inherits from object. Everything traces back to the same root.
Code objects and frame objects
Even the compiled bytecode that Python executes is an object. When Python parses a function, it creates a code object storing the bytecode, variable names, constants, and line number mappings.
def add(a, b):
return a + b
code = add.__code__
print(type(code))
print(code.co_varnames)
print(code.co_consts)The code object has type code. Its attributes describe the compiled structure of the function body, and tools that need a function's exact bytecode, constants, or variable names read this object directly instead of parsing source text.
<class 'code'>
('a', 'b')
(None,)The co_varnames tuple lists the local variable names. The co_consts tuple lists constants used inside the function. This level of introspection is what debuggers, profilers, and code analysis tools use behind the scenes.
Exceptions are objects
When an error occurs, Python creates an exception object. You can catch it, inspect its type and attributes, and even create exception objects without raising them.
try:
1 / 0
except ZeroDivisionError as e:
print(type(e))
print(id(e))
print(e.args)The exception has a type, identity, and arguments stored as attributes. Because an exception is a full object, you can inspect those attributes, log them, or re-raise the same instance later without losing that information.
<class 'ZeroDivisionError'>
4374012368
('division by zero',)You can store an exception object in a variable and inspect it later. This is why custom exception classes can carry arbitrary data: they are just objects that happen to be usable with the raise statement.
The practical value of this consistency
When everything is an object, the same inspection tools work on every value. The type function works on any value, and the dir function shows attributes on any object. The hasattr and getattr functions query attributes dynamically regardless of what kind of object you hold.
This consistency means Python's introspection capabilities are unusually powerful. You can write a function that accepts any object, inspects its type and attributes, and decides what to do at runtime. Frameworks like pytest, Django, and SQLAlchemy rely on this ability to discover test functions, map URL routes, and inspect model classes without explicit registration.
The object-oriented programming overview builds on this foundation. When you create your own classes, their instances follow the same rules as built-in objects. Your custom objects have identity through id(), a type through type(), and attributes accessible through dir() and dot notation, exactly like integers, strings, and functions do.
Rune AI
Key Insights
- Integers, strings, functions, classes, modules, and types are all full objects with identity, type, and attributes.
- You can call methods, inspect types, and check attributes on any Python value using the same tools.
- Functions and classes being objects is what enables decorators, first-class functions, and metaprogramming.
- The type of a type is
type, andtypeis itself an object, closing the hierarchy cleanly.
Frequently Asked Questions
Are integers really objects in Python?
What kinds of things are objects in Python?
Conclusion
Everything is an object in Python means exactly what it says. Numbers, strings, functions, classes, modules, types, and even code objects all share the same identity-type-value structure. This consistency is what makes Python's object model powerful: once you learn how objects work, you understand how every part of the language works.
More in this topic
How to Test Python Code
Learn what testing means in Python, the core ideas behind automated tests, and the built-in and third-party tools that make testing practical.
Write Your First Python Unit Test
Write and run your first Python unit test step by step, using unittest to check a small function and understand the test output.
The Factory Pattern in Python
Learn how to use the factory pattern in Python to encapsulate object creation, making your code more flexible and easier to extend.