You’re fine.
You can get pretty far with just functions. In fact, I would even argue that languages that force the developer to use classes for everything (such as Java) have a problem of “If the only tool you have is a hammer, every problem looks like a nail.”
Beginner programmers tend to write what I call “Choose your own adventure” code: it relies on flow control statements like loops, if/else branching, and functions. This is fine when you’re starting out.
As you learn data structures (even just lists and dictionaries) you’ll begin to see that creating a data model for something is better than the CYOA style. For example, if you want to make a chess program, you could represent the chess board as a dictionary: the keys can be strings that represent a space on the board (like 'A7'
or 'E3'
) and the values could be strings to represent the piece at that space (like 'bK'
for black king or 'wN'
for white knight.)
A lack of a 'A2'
key would mean there is no piece at the A2 space. Or maybe your chess dictionary does have keys for all 64 spaces on the board and 'A2': None
is a key-value pair that means A2 is empty. Or a key-value pair with a blank string like 'A2': ''
means A2 is empty. It just depends on what you think makes sense for your program.
This is how you model a real-world thing (the chess board and pieces) as data (a Python dictionary with string keys and string values).
Then instead of CYOA code, you write code that performs actions using this data. And here’s where classes come in: if you have a bunch of functions that always act on the same data structure, it can help to bundle that data and those functions into a class. (And we call functions in a class “methods”, even though they’re basically the same thing.)
For example, I have a free Python book where I wrote a small Tic Tac Toe program where I use a dictionary to represent the X’s and O’s on a Tic Tac Toe board: Chapter 15 - Object-Oriented Programming and Classes (see the " Non-OOP vs. OOP Examples: Tic-Tac-Toe" section)
Notice how a bunch of functions like isBoardFull()
and isWinner()
all take a “board” dictionary as their first argument. Since you’re always passing this same data structure to these functions, it makes sense to make a TicTacToeBoard
class that bundles the dictionary and these functions together. Then you can use the self
argument (called this
keyword in other languages) to reference that TicTacToeBoard
object’s data.
Classes are a way to organize your code. All of this is really only useful when your programs get into thousands of lines of code or more. You can read the rest of the chapter to understand “why do we have classes?”
But you can just keep using functions for now. Once your programs become large and kind of unwieldy and you start thinking, “Is there a better way to write this?” then the value of classes will be more apparent.