The object() built-in function returns a new featureless object that acts as the ultimate root base class for all Python classes.
Root Type & Sentinels: In Python, every class inherits from object, whether explicitly declared or not. Because bare object() instances lack a dynamic __dict__, they consume minimal memory and provide guaranteed unique memory identities via id(), making them ideal as unambiguous sentinel tokens. For runtime type inspection, see type(), or verify inheritance with isinstance() and issubclass().
The object() constructor takes no arguments and returns a new bare object instance:
# Formal syntax of built-in object()
featureless_instance = object()
| Parameter | Type | Default | Description & Operational Rule |
|---|---|---|---|
| None | Void | N/A | The object() constructor accepts zero arguments. |
| Return | object |
N/A | A new, featureless instance of the root built-in class. |
| Memory Footprint | 16 Bytes (64-bit CPython) | N/A | Occupies only the base PyObject header without an instance attribute dictionary. |
Every class in Python 3 inherits from object. Both built-in primitives and user-defined classes conform to this hierarchy:
# 1. The root of Python's class hierarchy
# All types and classes inherit from object
class CustomEntity:
pass
print("CustomEntity subclasses object: ", issubclass(CustomEntity, object))
print("int subclasses object: ", issubclass(int, object))
print("str subclasses object: ", issubclass(str, object))
# Every value in Python is an instance of object
print("100 is instance of object: ", isinstance(100, object))
print("'hello' is instance of object: ", isinstance("hello", object))Terminal Output:
CustomEntity subclasses object: True
int subclasses object: True
str subclasses object: True
100 is instance of object: True
'hello' is instance of object: True
Unlike instances of user-defined classes, a bare object() has no __dict__ attribute dictionary. Attempting to assign attributes raises an AttributeError:
# 2. Bare featureless objects have no dynamic __dict__
bare_obj = object()
print("Type of bare object: ", type(bare_obj))
print("Memory identity (hex): ", hex(id(bare_obj)))
# Attempting to assign attributes raises AttributeError
try:
bare_obj.custom_attr = "testing"
except AttributeError as err:
print("AttributeError caught: ", err)Terminal Output:
Type of bare object: <class 'object'>
Memory identity (hex): 0x7f9a12c418a0
AttributeError caught: 'object' object has no attribute 'custom_attr'
When writing functions where None is a valid user argument, using object() creates an unambiguous sentinel token that cannot be accidentally supplied by callers:
# 3. The Sentinel Pattern: distinguishing missing args from None
_MISSING = object() # Unique memory sentinel
def fetch_config_setting(key, default=_MISSING):
config_store = {"timeout": 30, "proxy": None}
if key not in config_store:
if default is _MISSING:
raise KeyError(f"Setting '{key}' not found and no default provided.")
return default
return config_store[key]
# Case A: Key exists with value None (distinguished from missing)
print("Proxy setting: ", fetch_config_setting("proxy"))
# Case B: Key missing with explicit fallback None
print("Missing fallback: ", fetch_config_setting("cache_dir", default=None))Terminal Output:
Proxy setting: None
Missing fallback: None
By default, bare object instances cannot be called like functions. Implementing the __call__() dunder method turns any class instance into a callable function-like object:
# 4. Implementing callable objects with the __call__() protocol
class Greeter:
def __init__(self, greeting_prefix):
self.prefix = greeting_prefix
def __call__(self, name):
# Enables calling instance directly as a function
return f"{self.prefix}, {name}!"
greet = Greeter("Welcome to plus2net")
# Invoking the instance directly with parentheses executes __call__()
print(greet("Alexander"))Terminal Output:
Welcome to plus2net, Alexander!
In high-throughput cache managers, a private sentinel token prevents redundant computation when cached functions return None:
# 5. Production Recipe: Sentinel-Driven Cache / Memoizer
class CacheStore:
def __init__(self):
self._store = {}
self._UNSET = object()
def get_or_compute(self, key, compute_func):
# Unambiguously checks if key was already computed
cached_val = self._store.get(key, self._UNSET)
if cached_val is not self._UNSET:
return cached_val, "CACHE_HIT"
computed = compute_func()
self._store[key] = computed
return computed, "COMPUTED"
cache = CacheStore()
val1, status1 = cache.get_or_compute("user_data", lambda: None)
val2, status2 = cache.get_or_compute("user_data", lambda: "never called")
print(f"Request 1 -> val: {val1}, status: {status1}")
print(f"Request 2 -> val: {val2}, status: {status2}")Terminal Output:
Request 1 -> val: None, status: COMPUTED
Request 2 -> val: None, status: CACHE_HIT
Because bare object() instances cannot hold arbitrary fields, use SimpleNamespace or a dataclass when a dynamic property bag is needed:
# 6. Common traps: bare object instantiation vs. custom classes
# Trap: instantiating bare object() when a simple namespace is needed
# Instead of object(), use SimpleNamespace or a dataclass for dynamic attributes
from types import SimpleNamespace
bag = SimpleNamespace(title="Product", quantity=50)
bag.status = "In Stock"
print("SimpleNamespace attributes:", bag.title, "|", bag.status)Terminal Output:
SimpleNamespace attributes: Product | In Stock
Explore class hierarchy roots, sentinel tokens, and callable objects in our interactive companion Jupyter Notebook:
Explore interactive code cells, benchmark sentinel comparisons, test callable instances, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify object() mechanics:
# 7. Self-verifying unit test suite for object()
# Test 1: object is its own type and root
assert issubclass(type, object)
assert isinstance(object, type)
# Test 2: Unique sentinel identity
s1 = object()
s2 = object()
assert s1 is not s2
assert s1 != s2
# Test 3: AttributeError on bare object attributes
try:
s1.foo = "bar"
assert False, "Should raise AttributeError"
except AttributeError:
pass
# Test 4: Default dunders present on object
obj = object()
assert hasattr(obj, "__repr__")
assert hasattr(obj, "__hash__")
assert hasattr(obj, "__eq__")
# Test 5: Callable protocol verification
class Counter:
def __init__(self): self.n = 0
def __call__(self): self.n += 1; return self.n
c = Counter()
assert c() == 1 and c() == 2
print("All object() unit assertions successfully verified!")Terminal Output:
All object() unit assertions successfully verified!
To optimize memory, CPython's base object does not allocate an attribute dictionary (__dict__), preventing dynamic attribute creation.
A unique singleton created with _SENTINEL = object() used to distinguish missing arguments from valid values like None or False.
Yes. In Python 3, all classes automatically inherit from object, making class MyClass: and class MyClass(object): completely identical.
Define the special method def __call__(self, *args, **kwargs): inside your class, enabling instances to be called like functions.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.