Dictionaries, Tuples, and Classes
00:00 In the previous lesson, I gave an overview of the course. In this lesson, I’m going to start the first section on records, structs, and data transfer objects, starting with dictionaries, tuples, and classes.
The purpose of record data structures is to group together fields that are related. This is sometimes known as a struct or a data object. Some programming languages like C actually have
struct as a keyword to do this kind of grouping. Frequently when you’re dealing with databases and ORMs, or object-relational models, these kinds of records map directly to the contents of a table in the database. This makes sense because whether you’re talking about a record or a class or a dictionary or a table in the database, you’re usually trying to group together fields that are of the same purpose.
If I’m describing a person and I need to know their first name, their last name, and their address, then there’s a table or an object named
Person and it has fields for the first name, last name, and address.
A quick and convenient mechanism for creating this kind of relationship in Python is to use the built-in
dict type as a dictionary, grouping fields together for a record. Consider this dictionary.
01:35 There are pros and cons of using a dictionary. Because it’s a built-in type for Python, it’s actually been quite optimized and it’s a convenient way of quickly putting something together. The fields themselves are dynamic, and that can be a bit of a problem. That means there’s no type checking.
There’s also no way of indicating mandatory fields. I could create another dictionary for
car2, forget the color, and then have my program throw an exception when I go to use the color of
car2. And finally, spelling errors can cause tricky-to-find bugs. Personally, I come from one of those countries that spells color differently than what’s in that dictionary above.
These kinds of bugs usually aren’t found until runtime and typically are found because of a
KeyError being thrown. The dictionary isn’t the only built-in type that allows you to group fields together.
Two tuples of the same length don’t necessarily have the same fields. My first tuple could have
'number', and my second tuple could have
'number' and then
'color'—and those aren’t going to work together.
collections library has a function that is a factory for a type of class called a
namedtuple. This allows you to create tuples where each of the positions in the tuple corresponds to an actual field.
03:33 The object-oriented aspects of Python allow you to create classes to group things together. Using a class is a little more formal than a dictionary, but it still has some of the problems the dictionary has. For example, there is no way to prevent the addition of fields for a class, just like there isn’t in the dictionary.
.__repr__() method of a class is pretty useless and doesn’t give you very much information, so you need to override that if you’re going to write a good class, so that’s more work that you have to do if you’re going to use a class as a record. Classes do support the
@property decorator, so you can create the concept of a read-only value.
04:08 So if that’s important to you, this is a feature that you can’t do with a dictionary. So there’s more control here, but a lot more work that you have to do to get it going. Typically, classes are only used if you’re going to include business logic in methods.
car.py defines the
Car class. Inside of the constructor, you can see the three fields that I intend to use:
.automatic. Notice that I’m casting each of the fields into the type that I’m expecting to come in from the constructor.
By doing this, I’m guaranteeing a certain degree of type safety. It’s not perfect, but it’s better than nothing at all. I don’t guarantee that what you pass in in the
.color field is actually a color, but I can guarantee later on that when I go to use it, it’ll be a string.
I’m doing something else with the
._mileage field as well, and that’s the leading underscore (
_). Python has no concept of public or private members of a class, but by convention, anything with a leading underscore isn’t meant to be publicly exposed. By putting the underscore here, I’m indicating to other programmers that I don’t intend for others to be using this field directly.
And the reason I’ve done this is because this class supports different ways of getting at the fuel economy of the car. Inside of the
.mpg property, it uses the
._mileage field directly. Inside of the
.km_per_liter property, I convert mileage into kilometers per liter.
Both of these are ways of measuring fuel efficiency, and by using this underscore on
._mileage, I’m indicating to other programmers that they should be using the
.km_per_liter properties instead. Finally in this class, I’m overriding the
The default one for a class gives you very little information. Instead, I want to print out information about the class itself. The convention in Python is to have your
.__repr__() method return a string that, if it were run in the REPL, would create the object that you are currently using.
The second field is expecting a float. When I pass in a string, that string can’t be cast to a float and so I get a
ValueError. Python is a dynamic language and provides no mechanism for preventing the addition of new fields.
Become a Member to join the conversation.