I sometimes encounter problems like
TypeError: unhashable type: 'list' while programming. Therefore, I decide to set aside some time to undertand the important Python concepts, mutable, hashable and iterable.
1. mutable and immutable
Python represents all its data as objects. Objects are identified by an unique and constant integer during its lifetime. Using the build-in function
id(object) returns the identity for a given object.
Python objects can be categorized into two types: mutable and immutable. For mutable objects, its content can be altered without changing their identity. One trick to check if a type is mutable or not is to use
id(object). For instance,
# immutable type, str() >>> s = 'abc' >>> id(s) 140125615331648 >>> s += 'def' # a new object is created >>> id(s) 140125614721616 # the id is changed # mutable type, set() >>> s = set(['a', 'b']) >>> id(s) 140125614845184 >>> s.add('c') >>> id(s) 140125614845184
Note that changing the content of an immutable object results in creating a new object. (A new object has to be created if a different value has to be stored)
The principal built-in types in Python are numerics, sequences, mappings, classes, instances and exceptions.
(1) immutable types
- immutable sequences:
(2) mutable types
- mapping types:
- classes, instances and exceptions
2. hashable and unhashable
The detailed description of hashable is excerpted from Python documentation: Glossary.
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__()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.
All of Python’s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Objects which are instances of user-defined classes are hashable by default; they all compare unequal (except with themselves), and their hash value is derived from their
3. iteration, iterable and iterator
Excerpt from :
Iteration is a general term for taking each item of something, one after another. Any time you use a loop, explicit or implicit, to go over a group of items, that is iteration.
An iterable is an object that has an
__iter__method which returns an iterator, or which defines a
__getitem__method that can take sequential indexes starting from zero (and raises an
IndexErrorwhen the indexes are no longer valid). So an iterable is an object that you can get aniterator from.
An iterator is an object with a
next(Python 2) or
__next__(Python 3) method.
Whenever you use a
forloop, or map, or a list comprehension, etc. in Python, the
nextmethod is called automatically to get each item from the iterator, thus going through the process of iteration.
A good example from  to explain those concepts.
>>> s = 'cat' # s is an ITERABLE # s is a str object that is immutable # s has no state # s has a __getitem__() method >>> t = iter(s) # t is an ITERATOR # t has state (it starts by pointing at the "c" # t has a next() method and an __iter__() method >>> next(t) # the next() function returns the next value and advances the state 'c' >>> next(t) # the next() function returns the next value and advances 'a' >>> next(t) # the next() function returns the next value and advances 't' >>> next(t) # next() raises StopIteration to signal that iteration is complete Traceback (most recent call last): ... StopIteration >>> iter(t) is t # the iterator is self-iterable
Excerpt from iterable:
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as
tuple) and some non-sequence types like
file objects, and objects of any classes you define with an
Iterables can be used in a
forloop and in many other places where a sequence is needed (
map(), …). When an iterable object is passed as an argument to the built-in function
iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call
iter()or deal with iterator objects yourself. The
forstatement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop.
Excerpt from interator:
When no more data are available, a
StopIterationexception is raised instead. At this point, the iterator object is exhausted and any further calls to its
__next__()method just raise
Iterators are required to have an
__iter__()method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted.
One notable exception is code which attempts multiple iteration passes. A container object (such as a
list) produces a fresh new iterator each time you pass it to the
iter()function or use it in a
forloop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.