The Decision to Use Classes

00:00 In the previous lesson, I gave an overview of the course. In this lesson, I’ll talk about when and when not to use an object-oriented approach in Python.

00:10 If you come from a language like Java where everything must be a class, your first tendency in Python might be very Java-like, but Python enables several different coding styles: procedural, which is script-like coding; functional, which uses things like lambdas and map/reduce; and what all the parts of this course have been about, object-oriented concepts.

00:34 Having this much choice is powerful, but you know that Spider-Man line about great responsibility? Choosing the wrong techniques may overcomplicate your job as a programmer.

00:45 Let’s briefly talk about a couple of other popular languages. If you’re coming from Java—or quite frankly, C++, and although I’ve never used it, I suspect C# falls into this bucket as well—simple programs don’t need classes at all. In Python, you should write classes when writing a class is a natural extension of the data you’re encapsulating.

01:07 If you have some data attributes you need to keep together and some operations that are dedicated to them, then you might want a class. For shorter programs, you can often get away with using a dictionary, a tuple, or more specifically a namedtuple, or now that Python has data classes, that might also be a good compromise. If you’re coming from JavaScript, welcome to a saner world. Sorry, just couldn’t help but get that dig in.

01:33 Classes in JavaScript are prototype based. There are a couple of other languages that use this approach as well, but they’re not as common. Python, like most other object-oriented languages uses a structured declarative approach.

01:47 If you’re used to ES6-style JavaScript using the class keyword, you won’t notice much difference syntactically, but the underlying mechanics of inheritance are different, so you might need to be careful as you dig in.

02:00 And since JavaScript was also originally a procedural language with object-oriented semantics layered on top, the easy mixing in Python will be familiar to you.

02:11 Unlike most other object-oriented languages, Python does not strictly enforce the concept of private or protected. All things are accessible. There is a convention that members with a leading underscore are non-public and users of your class shouldn’t rely on those kinds of attributes, but it is just a convention.

02:32 They can still be used through the interface.

02:36 There’s also some weird black magic—okay, gray magic, dirty white, off-beige magic—where double underscores cause a member to be name mangled. That’s a fancy way of saying it is automatically renamed.

02:49 This kind of works like private in that it hides it away, but if you know how to produce a mangled name, you can still call it. You probably shouldn’t, but the language doesn’t prevent you.

03:00 It’s sort of a Thin Ice sign rather than a fence. You can ignore it, but there might be some consequences.

03:08 Python has had object-oriented concepts for a long time, but it changed how they work in Python 2.2. In that version, oh so long ago, the underlying hierarchy was changed.

03:20 So the ultimate ancestor of your class in Python 2.1 and 2.2 are different. For backwards compatibility, 2.2 allowed you to declare a class using either the old style or the new style.

03:33 To indicate you wanted a new-style class, you inherit from the object class. To declare a person using the new class, it looks like this, where a Person inherits from object. Of course, the new in the phrase new-style is rather dated.

03:50 It’s from very long ago. The compatibility supporting both old and new styles was available all the way up to 2.7. Since the end of life of Python 2.7 was in 2020, that new thing continued to exist for almost two decades.

04:10 By contrast, Python 3 only uses the new style of classes.

04:14 The old style from before Python 2.2 no longer works. That made the whole inherit-from-object thing seem a little verbose, so its need was dropped. You can still use that syntax if you’d like, especially if you’re writing code that has to be compatible with Python 2 and 3. Essentially, in Python 3, both of these declarations result in the same thing. This, of course, can cause some confusion.

04:41 If you use the Python 3 style without the object in Python 2, you’re not going to get the old new style, but the actual very old, old style and some rather unexpected class behaviors.

04:55 I get why it’s done. It definitely looks better without the object, but this is a massive foot gun for folks maintaining older code, so be careful if you’re still straddling the Python 2/Python 3 world.

05:08 Okay, enough history and potential toe removal. When should you use a class? Well, it might be easier to outline when not to.

05:17 If you have a small script that doesn’t have a lot of data structures or you need really speedy code, if the code base you’re maintaining already doesn’t, for example, it’s heavily functional or the code is mostly procedural, your teammates will like you better if you stay consistent with the code that exists already. If you’re writing object-oriented code, you don’t necessarily have to use inheritance to gain the advantage of code reuse. Inheritance can be described as an is-a relationship.

05:48 For example, a car is a vehicle, so I might have my Car class extend my Vehicle class.

05:55 One alternative to this structure is a has-a relationship. This is also known as composition. This is the case where the attribute of one object references another.

06:08 You can still do duck typing and polymorphic interfaces using this mechanism, and there aren’t any surprises. When someone changes a base class—say, adding a method—that trickles down to the children. With composition, that won’t affect you unless you want to call it on the related object.

06:25 There are some special kinds of composition that have their own names. Delegation is composition with execution handoff. Your object references another object, and the owner has methods within which it calls the delegated object.

06:41 It’s a kind of wrapper. If you took part two of this course, the HexColorContainer example was a list delegate exposing its own interface, but implementing the underlying storage with a reference to a non-public list.

06:54 This was a kind of delegation.

06:58 An even more specific kind of composition is dependency injection. It’s still a wrapper, but this time what you’re wrapping is an object that is passed in.

07:08 Each call gets proxied to the injected object. This means you can change the behavior of the wrapping class by passing in—injecting—a different object. This pattern can be useful when you want to perform similar tasks on related objects. For example, if you’ve got a class that abstracts database interactions, injecting an object that implements specific database details allows you to code against the generic interface while someone else worries about how it is done.

07:38 The big advantage here is someone else can write another injectable object that implements the right interface—say, for yet another database—and everything will still work.

07:47 This pattern can be very useful for writing more testable code. Testing your generic database object is hard without actually touching a database, but if you injected a fake database implementation, you could test the wrapper with this delegate.

08:02 This is kind of what mock classes are doing for you. This pattern actually makes me a little uncomfortable. It isn’t really the pattern’s fault, but my own experience.

08:13 I’ve bumped into a couple of coders in my life who thought this approach was a religion and thought you should inject all the things. If the only use of a baseball bat you ever saw was it smacking you in the face, it wouldn’t be a surprise if you flinched watching the New York Yankees.

08:29 So, injection kind of makes me flinch.

08:33 Object-oriented coding started to come into its own in the 1980s. At the time, there were different languages out there experimenting with the right way of doing things and plenty of academic papers on the subject.

08:45 The first version of C++ was actually a transpiler converting C++ into C code before calling the C compiler. The work in this space resulted in a bunch of tenets describing the right way to do things.

08:58 SOLID is an acronym that covers five of these governing principles. The principles are single responsibility (it should do only one thing), the open-closed principle (you should be able to extend an object without mucking with its internals),

09:15 Liskov substitution principle (a harder way of saying duck typing), interface segregation principle (don’t put stuff in an object that won’t be used), and the dependency inversion principle (that says interacting objects should use an interface between them).

09:33 Some of this stuff may seem a little obvious if you’ve done a lot of object-oriented coding already, but it’s obvious because of the work done historically that makes it so.

09:43 SOLID is the work of several people. The concepts in SOLID, along with a bunch of others, were in a paper by a Robert C. Martin in 2000. Martin is better known as Uncle Bob in the programming world and was one of the creators of the Agile Manifesto. A few years later, Michael Feathers munged it together in an easier-to-remember acronym.

10:03 Those two don’t get all the credit. The Liskov substitution principle is named after Barbara Liskov, who came up with it. So it’s a real amalgamation of effort.

10:13 The rest of this course uses SOLID to talk about what good object-oriented coding looks like. All right, let’s dive into something solid. Well, that sounds ill-advised, doesn’t it?

Become a Member to join the conversation.