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.
Christopher Trudeau RP Team on Aug. 22, 2024
Hi Les,
The PosixPath
is part of the pathlib
module which contains a generic Path
object to describe a filename path on your computer. Posix is a standard used by Unix-based operating systems. The rules for filenames on Unix systems are different than the rules on Windows.
The pathlib
module uses inheritance to address this problem, with generic features like “name” in the base Path
class, while specifics like how to delete the associated file, inside the implementation classes for Unix and Windows.
My intent with this part of the lesson was more to show a real example of classes, object instances, attributes, and relations rather than the specifics of PosixPath
. Often in OO courses you only ever see generic things like Square
is a Rectangle
and was hoping to use a more concrete case than that.
I hope this clears it up a bit for you. If not, ask me anything and I’ll do my best to help.
Become a Member to join the conversation.
Les Burbury on Aug. 22, 2024
The PosixPath example was a bit confusing. what is it? The rest of the discussion is great. Clear, purposeful and honest but this part felt like an overlay concept that probably deserves more discussion.