Python Object
What is an Object
Object definition
The Python glossary defines an object as "Any data with state (attributes or value) and defined behavior (methods). Also the ultimate base class of any new-style class".
Everything we work with in Python is an object. If we use strings, integers, bytes, lists, dicts, etc. in our programs, we are using objects of a certain type. Classes and functions, user-defined or built-in, are also objects. When we have a file with the .py extension and import it, a module object is created. Built-in annotation types, exceptions, constants (False, True, None, NotImplemented, Ellipsis) are objects.
What is not an object?
1). Keywords, except False
, True
, and None
. The list of keywords depends on the Python version.
import keyword print(keyword.kwlist) # ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', # 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', # 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', # 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', # 'try', 'while', 'with', 'yield']
2). Delimiters such as (
, )
, [
, ]
, {
, }
, ,
(comma), :
, !
, .
, ;
, @
, =
, ->
, +=
, -=
, *=
, /=
, //=
, %=
, @=
, &=
, |=
, ^=
, >>=
, <<=
, **=
.
3). Operators such as +
, -
, *
, **
, /
, //
, %
, @
, <<
, >>
, &
, |
, ^
, ~
, :=
, <
, >
, <=
, >=
, ==
, !=
.
4). Other characters that have special meaning: '
, "
, #
, \
.
5) Literal prefixes and suffixes. They have special meaning only as part of literals.
- String literal prefixes: r
, u
, R
, U
, f
, F
, fr
, Fr
, fR
, FR
, rf
, rF
, Rf
, RF
.
- Bytes literal prefixes: b
, B
, br
, Br
, bR
, BR
, rb
, rB
, Rb
, RB
.
- Binary, octal, hexadecimal literal prefixes: 0b
, 0B
, 0o
, 0O
, 0x
, 0X
(Integer literals).
- Exponent in floating-point literals: e
, E
.
- Imaginary literal suffixes: j
, J
.
6) Escape sequences in string and bytes literals.
Characteristics of the Object
Type
Each object has a type. We can check this using type(object)
.
print(type("Python")) # <class 'str'> print(type(True)) # <class 'bool'> print(type(print)) # <class 'builtin_function_or_method'> print(type(list)) # <class 'type'> print(type([])) # <class 'list'> import json print(type(json)) # <class 'module'> def foo(): pass print(type(foo)) # <class 'function'> class A: pass print(type(A)) # <class 'type'>
Reference Count
Every object in Python has a reference count, that keeps track of the number of references that point to that object. When the reference count of an object drops to zero, it is deallocated.
There are also immortal objects. For example, True
and None
are immortal in CPython. An immortal object always exists as long as the interpreter is running.
# -1 because count includes a temporary reference in getrefcount(obj) import sys def foo(): pass print(sys.getrefcount(foo) - 1) # 1 foo1 = foo # additional reference print(sys.getrefcount(foo) - 1) # 2
For some objects, the returned value may not reflect the actual number of references to the object.
Size
Size of an object in bytes (vary by implementation and version of Python).
Python containers such as list, dict, set, etc. contain references to other objects (container elements). Using sys.getsizeof
does not take into account the memory consumption of the objects referenced in the container.
import sys print(sys.getsizeof(list)) # 424 print(sys.getsizeof([])) # 56
id()
All objects have an integer id that is unique and constant for those objects throughout their lifetime. In the CPython implementation, this is the address of the object in memory.
obj = [] obj1 = [] print(id(obj) == id(obj1)) # False
Immutable objects can be reused to optimize memory usage.
obj = 12 obj1 = 12 # same id print(id(obj) == id(obj1)) # True print(obj is obj1) # True
Equality and Hash
Almost all objects can be compared for equality, and they can have hash values.
The default behavior for equality comparison is based on the identity of the objects (x is y
implies that x == y
).
class A: pass class B: pass a = A() a1 = A() b = B() # same object, same identity print(a is a, a == a) # True True # different objects of same type (A) print(a is a1, a == a1) # False False # different objects of different types (A and B) print(a is b, a == b) # False False
Built-in types customize their comparison behavior to provide value-based equality within the same type (numbers, strings, binary sequences, lists, tuples, ranges, dicts, sets, etc.). User-defined classes can also customize their comparison.
Numeric types can be compared with other numeric types.
from decimal import Decimal obj = 1 # int obj1 = 1.0 # float obj2 = Decimal(1) # different objects of different types print(obj is obj1, obj is obj2, obj1 is obj2) # False False False # same value print(obj == obj1, obj == obj2, obj1 == obj2) # True True True
Containers of the same type can be compared by value.
obj = [1, 2, 3] obj1 = [1, 2, 3] print(obj is obj1) # different objects # False print(obj == obj1) # same value # True
Truth Value Testing
Any object can be tested for truthiness.
By default, an object is considered true unless its class defines a __bool__
method or a __len__
method.
def foo(): pass class A: pass a = A() print(bool(foo)) # True print(bool(A)) # True print(bool(a)) # True
Truth value testing of built-in types and PSL classes can differ from default truth value testing.
None
and False
;0, 0.0, 0j, Decimal(0), Fraction(0, 1)
;'', (), [], {}, set(), frozenset(), bytes(), bytearray(), range(0)
.list1 = [] list2 = [1, 2, 3] print(bool(list1)) # False print(bool(list2)) # True if list2: print(list2[0]) # 1
An object with an overridden __bool__
or __len__
methods.
class A: def __init__(self, value): self.value = value def __bool__(self): return bool(self.value) a = A("") print(bool(a)) # "" is empty sequence # False if a: print("a") else: print("not a") # not a
class A: def __init__(self, value): self.value = value def __len__(self): return len(self.value) a = A("") print(bool(a)) # len("") == 0 # False if a: print("a") else: print("not a") # not a
Mutable or Immutable
Objects can be mutable or immutable.
Built-in mutable types have methods for adding, setting, or removing values associated with them. They support item assignment, except for unordered types such as set. Changes to the value do not change the identity of the mutable object.
d = {"a": "A"} print(d) # {'a': 'A'} print(id(d)) # 1538355629632 d["b"] = "B" print(d) # {'a': 'A', 'b': 'B'} d.pop("a") print(d) # {'b': 'B'} print(id(d)) # 1538355629632
Built-in immutable types do not have methods for changing the values associated with them. They may have methods that manipulate the value, but these methods return a new object. If we need a new value, we get a new object with a new id().
s = "Python" print(id(s)) # 140725582104824 s1 = s.upper() print(s1) # PYTHON print(id(s1)) # 2651742964400 s2 = s.encode() print(s2) # b'Python' print(id(s2)) # 2651787375024
Object Attributes
Object attributes can be accessed through the dot notation: object.attr_name
. This is called an attribute reference.
We can check a list of attributes using dir(object)
. In most cases, dir(class_object)
and dir(instance_object)
show the same result.
# attributes of str type print(dir(str)) # ['__add__', '__class__', ..., 'upper', 'zfill'] # attributes of instance of class str print(dir("Python")) # ['__add__', '__class__', ..., 'upper', 'zfill']
Object attributes are also Python objects with type and attributes.
# __doc__ attribute of the upper() method print(type(str.upper.__doc__), str.upper.__doc__) # <class 'str'> Return a copy of the string converted to uppercase.
Unlike built-in classes and their instances, in user-defined classes and their instances we can have arbitrary values and functions. We can get, set, or delete these attributes. In this case, the attributes of the class and instance may differ.
class A: class_attr = "some shared value" def __init__(self, value): # value can be unique for each instance self.instance_attr = value def method(self): pass a = A(12) # attributes of A type print(dir(A)) # ['__class__', ..., '__init__', ..., 'class_attr', 'method'] # attributes of instance of class A print(dir(a)) # ['__class__', ..., '__init__', ..., 'class_attr', 'instance_attr', 'method']
References:
- Glossary: object
- Docs: Data model, Objects, values and types
- Docs: C-api PyObject and PyVarObject
- Docs: Type Object Structures
- GitHub: object.h
- Glossary: reference count
- Docs: sys.getrefcount
- PEP 683: Immortal Objects, Using a Fixed Refcount
- What’s New In Python 3.12: Immortal Objects
- Docs: sys.getsizeof
- Docs: Truth Value Testing
- Docs: Comparisons
- Docs: object.__attr__
- Docs: Built-in Types