Skip to content
HN On Hacker News ↗

Why OOP exists

▲ 22 points 63 comments by lumpa 1w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this entire text is human-written.

0 %

AI likelihood · overall

Human
100% human-written 0% AI-generated
SEGMENTS · HUMAN 1 of 1
SEGMENTS · AI 0 of 1
WORD COUNT 1,579
PEAK AI % 0% · §1
Analyzed
Aug 31
backend: pangram/v3.3
Segments scanned
1 windows
avg 1579 words each
Distribution
100 / 0%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 1,579 words · 1 segments analyzed

Human AI-generated
§1 Human · 0%

Introduction Welcome! This article will teach you the core ideas behind object-oriented programming, commonly known as OOP. This is the article I wish I read many years ago, when I was first learning about OOP in Python. If you're new to OOP, this article will explain why OOP exists, how it works, and how to work with OOP in Python. If you think you already know OOP, this article will change the way you think about programming and Python. Code is about real-world things Let me tell you about a programming project I had to do in college where you had to write a library management service. We were expected to work in pairs and I was paired with my good friend Tito. Tito and I sat down and started going through the problem statement, figuring out what we needed to implement. We got to a point where Tito turned to me and said: “It's not obvious to me what's the best way to represent a book in our program. Maybe you can start implementing the search functionality and I'll think about this for a while.” The search functionality was a set of functions that the problem statement required us to implement: find_by_title(catalog, search_term): returns a sublist with the books whose title contains the given search term find_by_genre(catalog, genre): returns a sublist with the books of the given genre find_by_author(catalog, author): returns a sublist with the books written by the given author I nodded, but then I thought about it for a second. If I don't know anything about how to work with books, there's no way I can implement these three functions. Tito agreed with me and told me he'd provide me with these functions: book_title(book): returns the title of the given book book_genre(book): returns the genre of the given book book_author(book): returns the author of the given book He told me to think of these functions as auxiliary functions that he would implement. I didn't have them yet, but I could write my search functions trusting he'd implement them correctly. In OOP, you have entities with associated data (books with authors, titles, and genres) and a set of functions to operate on those entities (the functions find_by_xxx). Now, think about it for a second. Can you implement the functions find_by_title, find_by_genre, and find_by_author, using the auxiliary functions that Tito will implement? How would you go about it? I worked on it for a bit, and eventually used a list comprehension to define the function find_by_title: def find_by_title(catalog, search_term): search_term = search_term.casefold() return [ book for book in catalog if search_term in book_title(book).casefold() ] The function find_by_title goes through the list of books called catalog with a loop and uses the auxiliary function book_title to retrieve the title. It then uses casefold to perform a case-insensitive search. Something worth noting here is that the loop uses the variable name book for the elements of the list catalog, even though I don't know what these “books” look like! But if Tito does his job well, the piece of code book_title(book) will return the book title and my code will work. I also implemented the other two functions following the same pattern: def find_by_genre(catalog, genre): return [ book for book in catalog if genre == book_genre(book) ] def find_by_author(catalog, author): return [ book for book in catalog if author == book_author(book) ] I finished my part of the assignment and I pinged Tito to see whether he'd made any progress. This assignment, the way Tito and I split our work up, and the way I wrote my part, all highlight the same thing: code is about real-world things. We're writing a library management system, so we need to be able to think about books, authors, and other related entities. When we're reading the code for our library management system, we want to be able to reason about the high-level, real-world entities that our code is about, like the books. That's why Tito and I decided to create an abstraction that represents a book. This simplifies my part of the code greatly. To let the rest of the program use the abstraction, we just need a few functions that expose useful operations. For example, Tito created the three functions book_xxx because he realised I'd need to access book information. These three functions hide the low-level details of how a book is implemented. The core idea of OOP is that you create high-level abstractions for the entities that you care about to make it easier to work with them. The O stands for Objects Tito worked on his representation of a book for a while and then he came back. He started by saying “hey Rodrigo, I realised we also need a function book_initialise(author, title, genre) that accepts three strings and creates the representation I came up with”. Then, he showed me his four functions: def book_initialise(author, title, genre): la = list(map(ord, author)) lt = list(map(ord, title)) lg = list(map(ord, genre)) return ((len(la), len(la) + len(lt)), la + lt + lg) def book_author(book): return "".join(map(chr, book[1][:book[0][0]])) def book_title(book): return "".join(map(chr, book[1][book[0][0]:book[0][1]])) def book_genre(book): return "".join(map(chr, book[1][book[0][1]:])) I was a bit surprised by Tito's code, but the truth is that it worked just fine: book = book_initialise("Charles Dickens", "Oliver Twist", "novel") print(book_author(book)) # Charles Dickens print(book_title(book)) # Oliver Twist print(book_genre(book)) # novel Still flabbergasted, I looked at Tito and wrote one more print: print(book) # ((15, 27), [67, 104, 97, 114, 108, 101, 115, 32, 68, 105, 99, 107, 101, 110, 115, 79, 108, 105, 118, 101, 114, 32, 84, 119, 105, 115, 116, 110, 111, 118, 101, 108]) “Tito, what the heck is this? When I print this book, all I see is numbers!” “That's because I thought it made sense to represent a book as a sequence of Unicode code points stored along with the cumulative lengths of the individual fields. But you don't have to worry about it, since the auxiliary functions book_xxx are the ones that represent the interface that users should interact with.” And while the internal representation of a book looked a bit weird, Tito was right about one thing. Since his auxiliary functions book_xxx were working, so were the find_xxx functions I wrote earlier: catalog = [ book_initialise("Charles Dickens", "A Christmas Carol", "novella"), book_initialise("Charles Dickens", "Oliver Twist", "novel"), book_initialise("Charles Dickens", "Great Expectations", "novel"), ] for book in find_by_title(catalog, "ol"): print(book_title(book)) # A Christmas Carol # Oliver Twist Once we finished working on books, we started working on authors. As it turns out, it's not enough to have a string with the author's name. An author depends on three pieces of information: first name last name birth year On top of that, there are a number of functions associated with authors: author_initialise(first_name, last_name, birth_year): builds the representation of an author based on the data provided author_name(author): returns the full name of the author author_birth_year(author): returns the author's birth year author_age(author): returns the current age of the author The funky book representation with Unicode code points and lengths is a bit contrived, so you can pick something more natural for an author. You can use a 3-element tuple to represent an author, as shown by the function author_initialise defined below: def author_initialise(first_name, last_name, birth_year): return (first_name, last_name, birth_year) If you think about the function author_initialise as a function that just creates a tuple with its arguments, the function will look quite useless. But that's not the power of the function author_initialise. This function is useful because it accepts three arguments and returns an author. How the author is represented is an implementation detail. At this point, it's clear that it's very important to be able to work with the objects that you create with your xxx_initialise functions. The concrete values of the high-level abstractions you work with in OOP are called objects. After implementing author_initialise, all other functions follow: def author_name(author): return f"{author[0]} {author[1]}" def author_birth_year(author): return author[2] def author_age(author): # Gross simplification, but bear with me: return 2026 - author_birth_year(author) Note that the first three functions have to access the internal representation of the author to be able to extract the most basic pieces of information. If you change how an author is represented, you have to change the functions author_name and author_birth_year. On the other hand, the function author_age builds on the most basic functions, and thus author_age is independent of the internal representation of an author. But now, as your programs grow and you start creating collections of functions to represent and work with different types of entities, like books and authors, you'll be riddling your programs with various functions with prefixes to help you organise them. The book-related functions are all called book_xxx, the author-related functions are all called author_xxx, and functions for other types of entities will likely follow a similar pattern. Many languages provide syntactic features that make it easier to work with OOP and these objects. You're now going to learn what features Python provides and you'll start by learning how to organise object-related functions. Organising functions with namespaces By now, it should be clear that it's useful to have functions to represent and manipulate real-world entities in your code. If you imagine for a second that OOP doesn't exist yet, you can see the value in structuring your programs around these objects, the representations of real-world entities like books and authors. So far, the only way to organise your functions was by giving them a shared prefix, such as author_xxx.