Python has many useful data structures. Among them, two need special attention: tuples (and arrays), and dictionaries. Tuples are defined using parenthesis and are immutable. They’re very nice to handle important data that will never change as the program runs, example the IP and port of a socket connection. Arrays, or lists, are defined using square brackets and are mutable. See Figure 5 for basic tuple and array use and proof that tuples are immutable.
Figure 5. Basic tuple and array use and proof that tuples are immutable
There are many functions of working with lists, if you are familliar with functional languages, you can map and reduce in Python too! And just to make things better, you can use list comprehensions to generate an entire new list when iterating over another. See Figure 6, 7 and 8 for some examples of these wonderful functions.
Figure 7. Reducing an array by summing each element to the other
Figure 8. Using a list comprehension to double each number in a array
Dictionaries are very wonderful data structures too. They provide, as the name says, key-value storage. You can initiate them previously using brackets and, in this case, you can set some key-value pairs. Access to values, addition of values, and the dictionary initiation are shown in Figure 9. Remember that dictionaries keys and values may be any kind of object.
Figure 9. Manipulating a dictionary
Functions
Functions can be defined using the def keyword and they can receive a finite number of arguments (including zero), a list of arguments, or a dictionary of arguments. See Figure 10 for a example a simple function which sum two arguments. Note that there’s no verification of type or anything like it. All the error handling regarding to the arguments type are the user’s responsibility.
Figure 10. A simple function to sum two numbers
Hey can even be assigned to a variable. Let’s say that you want to (don’t try to think why), temporarily replace some built-in function. You can assign the actual function to a variable, then replace the built-in with your own function, and put the original function back when you’re done. See Figure 11 for an example. Believe me, someday, this will save you.
Figure 11. Temporary replacing a built-in function for your own
Object Orientation
Object orientation is pretty straight forward in Python. You can define your own classes and inherit built-in classes, but you can’t modify built-in classes. The only thing that’s hard to understand in Python’s object- orientation is the self, but you don’t really need to understand why it’s there (it’s complicated), just accept its existence and don’t ever forget it.
Let’s create a simple “Duck” class with a “talk” method, create an instance and make it talk. See Figure 12 for the implementation.
Figure 12. Duck class implementation and use
That’s how you create class instances in Python. Note the “self” argument in the “talk” method. If you don’t put it there, you will get an argument error when trying to run the method. Now, let’s add a parameter to our Duck class and give a name to the duck. This can be done by implementing a method named __init__ inside the duck class. In Python, you will find many methods surrounded by underlines. They’re called magic methods and they have their purpose. The __init__ method is used when creating an instance of your class. So, we will add a name parameter there and set the object’s name attribute to its value. Figure 13 shows the implementation.
Figure 13. Putting a name attribute in our Duck class
By using self.name = name, we’re setting an attribute in our object. As Figure 13 shows, now you can access the duck’s name using the attribute. If we don’t use self there, the name variable would only be accessible inside __init__ method.
Do you want to learn something about those magic methods? They’re used to overload operators and even built-in functions called on your objects. For example, let’s create a Nest class for your Ducks and give it a special way to respond to the built-in len function.
Figure 14. A Nest class for your Duck class that responds in a particular way to the len function
When len is called on the Nest class, it will, under the hood, call the magic method __len__. It’s really magical! Now, let’s overload the sum operator of the Nest class to accept new ducks and only instances of the Duck class. Figure 15 holds the implementation.
Figure 15. Implementing operator overload through a magic method
Obviously, the magic method needs the other object as an argument to overload the sum operator, that’s why it’s there. Then, we used the isinstance built-in method to check if the object we’re trying to sum with the Nest is an instance of the Duck class. If positive, we add the duck to the duck list, otherwise, we should do something about it, like raise an error. We will see more about error raising and handling in the next topic.