The Case for Object-Oriented Programming

00:00 In the previous lesson, I gave an overview of the course. In this lesson, I’ll make the argument for the use of object-oriented coding. Who doesn’t love a good argument?

00:10 Let’s start by diving right into some code.

00:24 Consider this small function. It calculates the area of a circle, taking radius as an argument.

00:33 Whatever units 3 is in, the area is 28 of those very same units. For fun, let’s say light-years.

00:41 Consider if I wanted to do other things with that circle. I might create a dictionary with some data describing it. This dictionary describes both the circle’s radius and its color.

00:52 If I want to calculate the area,

00:59 I reference the radius key in the dictionary and pass it into the function. Over time, my code might grow. I might add a function for perimeter.

01:08 I might also add a square, and passing the length of a square side into the circle_area() function wouldn’t really give me a logical result.

01:17 Wouldn’t it be great if I could group these kinds of things together? Well, that’s the idea behind the object-oriented programming methodology. You use the language’s features to group data and operations on that data together. Of course, you could do this just by keeping everything together in the same file, but it would be helpful to have the compiler enforce some of the rules.

01:40 There’s another reason for the object-oriented approach. Let’s go back into the REPL and look at some data. Here. I’ve got a person. The person dictionary has two key-value pairs corresponding to the first and last name. Let’s create somebody else.

02:06 This is similar but slightly different. I’ve got a third property, the ID. Logically, "Yennefer" is both an employee and a person. If you want the concept of a person to also have a middle name, using data structures like this means making changes in two places.

02:24 This is the second key concept of object-oriented coding: structuring data in a way that is reusable. You also want some rudimentary verification as well.

02:35 Say I had a function that expected a dictionary with keys for first and last name. I could pass either a person or an employee and it would work, but if I misspelled "first" in Yennefer’s dictionary, it would fail.

02:48 Having the language ensure that can’t happen is helpful.

02:52 So not only do you want data and its associated operations together, but you want to be able to structure your data so it can be reused. This is sometimes referred to using the phrase is a.

03:04 The employee is a person, so everything in person should also be an employee. This mechanism is known as inheritance, where employee inherits the person’s attributes. If you did it this way, any changes to the person, like the addition of a middle name, would also show up in the employee as well.

03:25 Building code this way means better reuse and makes mistakes based on the form of the data less likely. Let’s go back to the REPL and look at an object like this in the standard library.

03:40 This is the PosixPath object from the pathlib module.

03:44 Examining the object, you see that it is a class. A class is what Python and most object-oriented languages calls the template that describes the structure of the data. The class doesn’t have any data in it.

03:57 It merely tells Python the form of the data and the methods that can be run upon it. That’s a little white lie, but that’s close enough for now. I’ll show you the details as the course goes on.

04:09 The thing that stores the data is an object, which you get by instantiating the class. In Python (and in most object-oriented languages), you do this by calling the class with parentheses.

04:25 You may have seen this mechanism without thinking about what was going on. PosixPath is a class. Using the parentheses on the class constructs an object.

04:34 In this case, that construction takes an argument, the name of a file. The result of constructing the class is a new object instance, which I’ve put in a variable named p.

04:47 The p object has attributes, which you access using dot notation. The fact that I called that an attribute is quite possibly a gross oversimplification bordering on a lie, but the details about how it works underneath aren’t important.

05:02 Dot notation on a member gives its value—in this case, the name of the file the object was constructed with. The object also has methods. These are like functions attached to the object.

05:15 You’ll sometimes hear people speaking about this loosely, but the strict definition is a function doesn’t belong to an object, while a method does. Just like a function, you can call a method using parentheses.

05:28 The .exists() method returns True if the file the PosixPath object is pointing to exists. In my case, it does not.

05:37 The general term for both attributes and methods is members, as in the members of the object or class. You can see all the members by passing an object to the built-in dirs() function.

05:50 That’s quite a lot, isn’t it? If you scan through here, you’ll find both the .name attribute and the .exists() method.

05:59 Okay, let’s review. I started with the PosixPath, which is a class. I then constructed it by calling it with parentheses, passing in "demo.py" as an argument.

06:11 This construction is called instantiation. The constructor returns an instance object, which I put in the variable named p. The instance object has attributes and methods.

06:26 In addition to the regular kind of members, Python also has special members, sometimes called magic members or dunder members. Dunder is short for double underscore, as these special members have a double-underscore prefix and suffix.

06:42 .__doc__ contains the docstring for the object. Yep, you can actually get the documentation associated with the code programmatically. It is kept as a special attribute on the object itself.

06:54 Get used to me calling this dunder. Not only is it the slang most Python programmers use, it’s much more fun to say. .__str__() is a special method that Python calls on the object when you convert it to a string.

07:08 If you print out p or pass it to the str() string function, Python calls this method, and whatever it returns is what gets printed or converted.

07:20 The PosixPath class is actually built using inheritance. There is a base class called Path that is a general implementation of path-like functionality. The PosixPath and WindowsPath classes extend Path, meaning they are Path classes having all the same attributes and methods as Path. PosixPath and WindowsPath implement Unix- and Windows-specific features, built on top of the base class.

07:48 They’re the employee to Path’s person.

07:53 Let’s summarize. Why might we use object-oriented coding? Code often relates to real-world things—people and file paths, for example. Keeping the data for these things and the operations on them together just kind of makes sense. By grouping things in classes, you can reuse the code.

08:12 The common code to both Unix and Windows file descriptors should be in one place. Only the stuff specific to Unix or Windows should be considered separately.

08:22 That way, if you find a bug in the common code, you only have to fix it in one place. This is part of the DRY principle, short for don’t repeat yourself.

08:33 Defining classes can provide an abstraction mechanism. That means an object can have a well-known interface that the users of that object just need to know in order to use it.

08:44 If both person and employee have the first and last name attributes, I can operate on them as if they’re equivalent without worrying about what else they contain. This leads to a degree of flexibility.

08:57 You can build interfaces where the coder doesn’t really care about the underlying implementation as long as the interface is understood. You may have heard the phrase duck-typing in Python.

09:08 It refers to the fact that if it looks like a duck and talks like a duck, you can use it like a duck, regardless of whether it is a swan in disguise or not.

09:19 A related fancy academic term for this is polymorphism: poly meaning many and morph meaning forms. A common example of this in Python—and Unix in general—is file-like behavior. If you implement the file protocol methods, there are all sorts of libraries that can use your object, which don’t care whether you’re reading and writing to the local disk or to some remote S3 server.

09:44 You abstract the details away and just pass around your duck.

09:49 Enough abstractness and abstraction. Next up, you’ll write some code.

Become a Member to join the conversation.