6.4. Iterable Nested

  • Iterable is an object

  • Iterable element is an object too

  • Therefore an element of a Iterable could be another Iterable

  • There is no limit how nested it could be

>>> obj = 1
>>>
>>> data = [obj, obj, obj]
>>> data
[1, 1, 1]
>>> obj = [1, 2, 3]
>>>
>>> data = [obj, obj, obj]
>>> data  
[[1, 2, 3],
 [1, 2, 3],
 [1, 2, 3]]

6.4.1. What is an Object?

  • Basic types are objects

  • Iterable are objects too

  • Everything is an object

>>> int.mro()
[<class 'int'>, <class 'object'>]
>>> float.mro()
[<class 'float'>, <class 'object'>]
>>> bool.mro()
[<class 'bool'>, <class 'int'>, <class 'object'>]
>>> type(None).mro()
[<class 'NoneType'>, <class 'object'>]
>>> tuple.mro()
[<class 'tuple'>, <class 'object'>]
>>> list.mro()
[<class 'list'>, <class 'object'>]
>>> set.mro()
[<class 'set'>, <class 'object'>]

6.4.2. List of Lists

Also known as multidimensional lists or matrix.

Readability differs depending on whitespaces:

>>> a = [[1,2,3],[4,5,6],[7,8,9]]
>>> b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> c = [[1,2,3], [4,5,6], [7,8,9]]
>>> d = [
...      [1, 2, 3],
...      [4, 5, 6],
...      [7, 8, 9],
... ]
>>> e = [
...      [1, 2, 3],
...      [4, 5, 6],
...      [7, 8, 9]]
>>> f = [[1, 2, 3],
...      [4, 5, 6],
...      [7, 8, 9],
... ]
>>> g = [[1, 2, 3],
...      [4, 5, 6],
...      [7, 8, 9]]

6.4.3. List of Tuples

Readability differs depending on whitespaces:

>>> data = [(4.7, 3.2, 1.3, 0.2, 'setosa'),
...         (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...         (7.6, 3.0, 6.6, 2.1, 'virginica')]
>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica')]
>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]

6.4.4. List of Dicts

>>> data = [
...     {'sepal_length': 5.4, 'sepal_width': 3.9, 'petal_length': 1.3, 'petal_width': 0.4, 'species': 'setosa'},
...     {'sepal_length': 5.9, 'sepal_width': 3.0, 'petal_length': 5.1, 'petal_width': 1.8, 'species': 'virginica'},
...     {'sepal_length': 6.0, 'sepal_width': 3.4, 'petal_length': 4.5, 'petal_width': 1.6, 'species': 'versicolor'},
... ]
>>> data = [
...     {'measurements': [4.7, 3.2, 1.3, 0.2], 'species': 'setosa'},
...     {'measurements': [7.0, 3.2, 4.7, 1.4], 'species': 'versicolor'},
...     {'measurements': [7.6, 3.0, 6.6, 2.1], 'species': 'virginica'},
... ]

6.4.5. Many Types

Readability differs depending on whitespaces:

>>> data = [
...     [1, 2],
...     (3, 4, 5, 6),
...     {7, 8, 9, 10, 11},
... ]

Content could be both basic types and sequences:

>>> data = [
...     1,
...     1.0,
...     True,
...     None,
...     'one',
...     [1, 2],
...     (3, 4, 5, 6),
...     {7, 8, 9, 10, 11},
... ]

Content could be even more complex data structure with nested items:

>>> data = {
...     'firstname': 'Mark',
...     'lastname': 'Watney',
...     'birthdate': '2000-01-02',
...     'age': 24,
...     'cities': ('Houston', 'Pasadena'),
...     'states': ['Texas', 'California'],
...     'groups': {'users', 'staff', 'admin'},
...     'friends': [
...         {'firstname': 'Melissa', 'lastname': 'Lewis'},
...         {'firstname': 'Rick', 'lastname': 'Martinez'},
...         {'firstname': 'Alex', 'lastname': 'Vogel'},
...         {'firstname': 'Beth', 'lastname': 'Johanssen'},
...         {'firstname': 'Chris', 'lastname': 'Beck'},
...     ],
... }

6.4.6. Length

list[list]:

>>> data = [[1, 2, 3],
...         [4, 5, 6],
...         [7, 8, 9]]
>>>
>>> len(data)
3
>>> len(data[0])
3

list[tuple]:

>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>> len(data)
3
>>> len(data[0])
5

list[dict]:

>>> data = [
...     {'measurements': [4.7, 3.2, 1.3, 0.2], 'species': 'setosa'},
...     {'measurements': [7.0, 3.2, 4.7, 1.4], 'species': 'versicolor'},
...     {'measurements': [7.6, 3.0, 6.6, 2.1], 'species': 'virginica'},
... ]
>>>
>>>
>>> len(data)
3
>>>
>>> len(data[0])
2
>>>
>>> len(data[0]['measurements'])
4
>>>
>>> len(data[0]['species'])
6

6.4.7. Append vs Extend

>>> data = [1, 2, 3]
>>> data.extend([4, 5, 6])
>>>
>>> data
[1, 2, 3, 4, 5, 6]
>>> data = [1, 2, 3]
>>> data.append([4, 5, 6])
>>>
>>> data
[1, 2, 3, [4, 5, 6]]

Append elements using list.append():

>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>> row = (4.9, 2.5, 4.5, 1.7, 'virginica')
>>>
>>> data.append(row)
>>> data  
[(4.7, 3.2, 1.3, 0.2, 'setosa'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor'),
 (7.6, 3.0, 6.6, 2.1, 'virginica'),
 (4.9, 2.5, 4.5, 1.7, 'virginica')]

Append elements using list.extend():

>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>> row = (4.9, 2.5, 4.5, 1.7, 'virginica')
>>>
>>> data.extend(row)
>>> data  
[(4.7, 3.2, 1.3, 0.2, 'setosa'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor'),
 (7.6, 3.0, 6.6, 2.1, 'virginica'),
 4.9,
 2.5,
 4.5,
 1.7,
 'virginica']

Extend with many rows:

>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>> rows = [
...     (4.9, 2.5, 4.5, 1.7, 'virginica'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor')
... ]
>>>
>>> data.extend(rows)
>>> data  
[(4.7, 3.2, 1.3, 0.2, 'setosa'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor'),
 (7.6, 3.0, 6.6, 2.1, 'virginica'),
 (4.9, 2.5, 4.5, 1.7, 'virginica'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor')]

Append with many rows:

>>> data = [
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor'),
...     (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>> rows = [
...     (4.9, 2.5, 4.5, 1.7, 'virginica'),
...     (7.0, 3.2, 4.7, 1.4, 'versicolor')
... ]
>>>
>>> data.append(rows)
>>> data  
[(4.7, 3.2, 1.3, 0.2, 'setosa'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor'),
 (7.6, 3.0, 6.6, 2.1, 'virginica'),
 [(4.9, 2.5, 4.5, 1.7, 'virginica'), (7.0, 3.2, 4.7, 1.4, 'versicolor')]]

6.4.8. Use Case - 0x01

One dimensional (1D) structure - vector:

>>> from pprint import pprint
>>> obj1 = 1
>>> obj2 = 2
>>> obj3 = 3
>>>
>>> data = [obj1, obj2, obj3]
>>>
>>> pprint(data, width=20)
[1, 2, 3]

Two dimensional (2D) structure - matrix:

>>> obj1 = [1, 2, 3]
>>> obj2 = [4, 5, 6]
>>> obj3 = [7, 8, 9]
>>>
>>> data = [obj1, obj2, obj3]
>>>
>>> pprint(data, width=20)
[[1, 2, 3],
 [4, 5, 6],
 [7, 8, 9]]

Three dimensional (3D) structure - tensor:

>>> obj1 = [[1,2,3], [4,5,6], [7,8,9]]
>>> obj2 = [[10,20,30], [40,50,60], [70,80,90]]
>>> obj3 = [[100,200,300], [400,500,600], [700,800,900]]
>>>
>>> data = [obj1, obj2, obj3]
>>>
>>> pprint(data, width=20)
[[[1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]],
 [[10, 20, 30],
  [40, 50, 60],
  [70, 80, 90]],
 [[100, 200, 300],
  [400, 500, 600],
  [700, 800, 900]]]

6.4.9. Assignments

"""
* Assignment: Iterable Nested Create
* Type: class assignment
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
    1. Create nested list `result` with elements:
        a. tuple: 1, 2, 3
        b. list: 1.1, 2.2, 3.3
        c. set: 'red', 'green', 'blue'
    2. Run doctests - all must succeed

Polish:
    1. Stwórz zagnieżdżoną listę `result` z elementami:
        a. tuple: 1, 2, 3
        b. list: 1.1, 2.2, 3.3
        c. set: 'red', 'green', 'blue'
    2. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert result is not Ellipsis, \
    'Assign your result to variable `result`'
    >>> assert type(result) is list, \
    'Variable `result` has invalid type, should be list'
    >>> assert len(result) == 3, \
    'Variable `result` length should be 3'

    >>> assert (1, 2, 3) in result
    >>> assert [1.1, 2.2, 3.3] in result
    >>> assert {'red', 'green', 'blue'} in result
"""

# Result should contain:
# - tuple: 1, 2, 3
# - list: 1.1, 2.2, 3.3
# - set: 'red', 'green', 'blue'
# type: list[tuple|list|set]
result = ...