Iterable and hashable in Python
Hashable
An object is hashable if it has a hash value which never changes during its lifetime (it needs a hash() method), and can be compared to other objects (it needs an eq() or cmp() method). Hashable objects which compare equal must have the same hash value.
Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.
To check if an object is hashable or not and find out its hash value (if it’s hashable), we use the hash() function on this object:
print(hash(3.14)) Output: 322818021289917443.
Almost all immutable objects are hashable (we’ll see soon a particular exception) while not all hashable objects are immutable.
In particular, all the primitive data types (strings, integers, floats, complex, boolean, bytes), ranges, frozensets, functions, and classes, both built-in and user-defined
, are always hashable, while lists, dictionaries, sets, and bytearrays
are unhashable.
We see that Python tuples can be either hashable or unhashable. They are unhashable only if they contain at least one mutable item.
Iterable
Iterable is an object which can be looped over or iterated over with the help of a for loop.
Objects like lists, tuples, sets, dictionaries, strings, etc. are called iterables. In short and simpler terms, iterable is anything that you can loop over.
Iterables: list1 = [1,2,3,4,5]
- Can be iterated using for loop.
- Iterables support iter() function.
- Iterables are not Iterators.
Iterators: list2 = iter(list1), next(list2)
- Can be iterated using for loop.
- Iterators suppports iter() and next() function.
- Iterators are also Iterables.
Ordered
Ordered objects in Python are those iterable objects where a determined order of items is kept unless we intentionally update such objects (inserting new items, removing items, sorting items).
Ordered objects are strings, lists, tuples, ranges, and dictionaries (starting from Python 3.7+).
Unordered — sets, frozensets, and dictionaries (in Python versions older than 3.7).
In unordered objects, we can’t access individual items.
For dictionaries, use keys and values:
dct = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
print(list(dct.values())[0])
print(list(dct.values())[1:4])
print(list(dct.keys())[1:4])
print(dct['b'])
Mutable vs. Immutable
Mutable objects in Python are those objects that can be modified. Mutability doesn’t necessarily mean the ability to access individual items of a composite object by indexing or slicing
. For example, a Python set is unordered and unindexed and nevertheless, it’s a mutable data type since we can modify it by adding new items or removing items from it.
On the other hand, a Python tuple is an immutable data type, but we can easily access its individual items by indexing and slicing (without being able to modify them, though). Also, it’s possible to index and slice a range object extracting from it integers or smaller ranges.
Mutable: lists, dictionaries, sets, and bytearrays
Immutable: all the primitive data types (strings, integers, floats, complex, boolean, bytes), ranges, tuples, and frozensets.
Let’s explore one interesting caveat about tuples. Being an immutable data type, a tuple can contain items of a mutable data type, e.g., lists. The tuple above contains a list as its first item. We can access it but can’t re-assign another value to this item.
However, since a Python list is a mutable and ordered data type, we can both access any of its items and modify them:
tpl = ([1, 2], 'a', 'b')
tpl[0][0] = 10
Check the attribute
print(hasattr(3.14, '__iter__'))
print(hasattr('pi', '__iter__'))
lst = [1, 2, 3]
print(type(lst))
iter_obj = iter(lst)
print(type(iter_obj))