Beginners guide to Iteration in python
By: Keno Leon
Tags: Software Development, Data Science, Programming, Python, Coding

Small Recap - Pattern:

We can now make many things, give each one unique attributes when they are made, and modify them afterwards, this last two snippets focused in making this process more Object Oriented, which in our case means encapsulating individual methods and attributes in a RubberDuck class and leaving creation and interaction to our main script:

Object Definitions on the left and Object Instances on the right.

Iteration

We’ve already done some basic iteration going over our _ducks dictionary with for i in range(x) , but there are certainly other ways that do not depend on having a value for x , if for instance we wanted to change all our ducks there are other ways that iterate over the dictionary instead of a given value and this is the start of more complex iterations:

Iterate and change all the elements of our _ducks dictionary after creation: for i in _ducks:

_ducks[i].color = 'money'

Another way through key/values: for i, duck in _ducks.items():

duck.color = ‘rainbow'

Selecting ,Removing, Adding.

Very often when you have a bunch of things you want to select a few here and there based on certain requirements, this is where the object oriented approach is really helpful, in general we want to either filter or test for a certain object property (name, color, id, etc, etc )and that way we can select one or many, here’s an example:

# Turn Grey Ducks into White ones: for i in _ducks:

if _ducks[i].color == 'Grey':

_ducks[i].color = 'White' # Note that == tests equality and = assigns.

Let’s now remove any white rubber ducks named James :

for i in list(_ducks):

if _ducks[i].color == 'White' and _ducks[i].name == 'James':

del _ducks[i] This bit while seemingly simple has one gotcha, we are using a list before our dictionary, this is because we are iterating and changing the iteration elements, so we need to make a copy, read more about it here: How to avoid “RuntimeError: dictionary changed size during iteration” error?

Turns out James was on vacation and just came back, let’s reinsert him:

_ducks[1] = Rubber_Duck_Factory.RubberDuck()

_ducks[1].color = 'Tan'

_ducks[1].name = 'James' ⚠️ Note that we need to provide the index or slot _ducks[1], which requires us to keep track of what position or key in our dictionary James used to occupy, but we could also have added James somewhere else _ducks[111], it also doesn’t need to be a number, we could for instance have done _ducks[‘returningDuck’], the dictionary just doesn't care, next sections deal with this.

Just to reiterate the previous point, if you are using a dictionary to store objects, you will most likely be accessing your objects through a key, this might not be ideal if you need uniqueness, consider the following way (That doesn’t work ) of creating and storing objects:

#Try to create 5 ducks THIS DOESN'T WORK: for i in range(5):

tempDuck = Rubber_Duck_Factory.RubberDuck()

_ducks[tempDuck.name] = tempDuck # _ducks{} Dictionary : # {'Susan': <Rubber_Duck_Factory.RubberDuck object at 0x1041c5be0>, 'Oswald': <Rubber_Duck_Factory.RubberDuck object at 0x103d07080>, 'Maria': <Rubber_Duck_Factory.RubberDuck object at 0x103ce2ef0>}

We only got 3 ducks instead of the 5we wanted, why ? we are are creating a temporary duck tempduck , then using it’s name as a key for insertion into our dictionary, but the next duck that we make might have the same name ( in this case 2had the same names ) and then since it’s a dictionary replaces the existing ones.

A possible alternative is to use a unique object identifier :

THIS SOMETIMES WORKS: for i in range(5):

tempDuck = Rubber_Duck_Factory.RubberDuck()

_ducks[id(tempDuck)] = tempDuck # _ducks{} Dictionary : {4485467888: <Rubber_Duck_Factory.RubberDuck object at 0x10b5acef0>, 4485615744: <Rubber_Duck_Factory.RubberDuck object at 0x10b5d1080>, 4490585984: <Rubber_Duck_Factory.RubberDuck object at 0x10ba8e780>, 4490587104: <Rubber_Duck_Factory.RubberDuck object at 0x10ba8ebe0>, 4490587440: <Rubber_Duck_Factory.RubberDuck object at 0x10ba8ed30>, 4490587496: <Rubber_Duck_Factory.RubberDuck object at 0x10ba8ed68>, 4490587608: <Rubber_Duck_Factory.RubberDuck object at 0x10ba8edd8>} Note: 0x10b5acef0 is hex for 4485467888

I say it sometimes works because the ids are only unique while your program runs and you might need to keep track of the id’s somewhere else to select them.

To try and wrap up this unexpectedly complex issue of what key to use in a dictionary, it really depends on your data and what you are trying to make, you could use a timestamp, some long random number or a combination of object attributes, and if this sounds like overkill or too much, you might be better off using lists which we will briefly cover next.

Lists vs Dictionaries for Storing Objects

Our _ducks dictionary was working great until we decided to remove and then re add some element, it still works, but keeping track of where the removed element used to be might not work for your project, a list might be better suited to store objects where order plays an important role.

Don't feel bad if halfway through your project your data structure turns out to be the wrong one, this happens more often than we'd like to admit and is usually caused by us learning about the domain and/or language as we write, so it's part of the process, rectifying the situation is usually less of an issue if you use objects.

Let’s rewrite our examples with lists:

import Rubber_Duck_Factory # Now a List:

_ducks = [] # create 6 Ducks

for i in range(6):

_ducks.append(Rubber_Duck_Factory.RubberDuck()) # Iterate : for i, duckObject in enumerate(_ducks):

print (i, ",",duckObject) # OUTPUT: # 0 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA1308>

# 1 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA6208>

# 2 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA6448>

# 3 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA6948>

# 4 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA6BC8>

# 5 , <Rubber_Duck_Factory.RubberDuck object at 0x0000022FE7CA6CC8> for duck in _ducks:

print(duck.name + ' the ' + duck.color + ' ducky') # OUTPUT:

# James the Yellow ducky

# Maria the Blue ducky

# Susan the Red ducky

# Oswald the Pink ducky

# Peter the Grey ducky

# James the Blue ducky # Change all:

for duck in _ducks:

duck.color ='black' # OUTPUT:

# James the black ducky

# Maria the black ducky

# Susan the black ducky

# Oswald the black ducky

# Peter the black ducky

# James the black ducky # Select and Remove:

for duck in _ducks:

if duck.name == 'James' and duck.color == 'black':

_ducks.remove(duck) # OUTPUT:

# Maria the black ducky

# Susan the black ducky

# Oswald the black ducky

# Peter the black ducky



Our ducks are numbered now.

The changes are minimal and the iteration is very similar to a dictionary, lists though have a few methods that make them work better with ordered objects, for instance:

insert a new duck at the third slot.

_ducks.insert(2, Rubber_Duck_Factory.RubberDuck()) Note that the ducks reorder themselves after insertion.