What is a class in Python?
I've been learning a bit more Python, going through a Udemy course to expand my skills a little. One of the programs the course guides you to make is a little dictionary, but it currently only runs once and then quits.
I'd like to adapt it to use a nice TUI that keeps itself open until the user specifies they want to quit, using something along the lines of npyscreen. However, this library uses classes, and that's not something I'm yet familiar with. I'd rather have an understanding of what classes are, how they work, and why to use them before I take the plunge and start fiddling around with npyscreen (although I'd be interested to hear if you think that I should Just Do It instead).
Can anyone give or point me towards a good explanation of the what, how, and why of Python classes? Or better yet, a tutorial that will give me something to write and play with to figure out how it all fits together?
Thanks!
Lots of good description here, but my style of learning is through practical example code, so here's my take:
A class is a way a way to gather several piece of information and functionalities at the same place (usually because it makes sense to put them at the same class). The way you define a class is similar to how you define a function except parenthesis is not required (it has a different meaning compared to the way you define function). Consider this class:
This class has a name (
Student
), two methods (say_identity
andchallenge_something
) and a constructor (__init__
). I assume you are familiar with the function construct (def
something something). You may notice that the method and constructor looks awfully like a function. That's because they are function, only they are special ones that have a particular relationship with their class. And also, there's thisself
keyboard that gets colored all over the place. What's the deal with them ? Look at what I can write on the console and what are the effects:In this example, I have defined my class in the first prompt. In the second prompt I have created an instance of the class
Student
. An instance is a specific version of class, separated from the other instance. In this particular case I've created an instance ofStudent
with the nameharry
, whose full name isHarry Potter
and whose school isHogwarts School of Witchcraft and Wizardry
. Those variable (full_name
andschool
) are calledattributes
. All attributes are common (in the sense that they exists) throughout all classes, but their values can be different. For example we can very well model another Student like so:Attributes are usually but not always set with a constructor, which is a special method that, well, construct an instance of a class. That's what this piece of code
is for. The
self
keyword is a reference to the current object and is the main way you can interact with the current attributes and the method of a class from within a method. Notice that when I askedharry
to say who he was (harry.say_identity()
), I didn't need to specify theself
thingy. It's essentially invisible when you invoke a method (look at howchallenge_something
is used), but it is still required when you need to reference member of the class (an attribute or a method)(look at howchallenge_something
is re-using thesay_identity
method).So Class are a neat way to tidy up and simplify your code, but there's another layer that let's you do more with less code: inheritance. Consider this:
Here we have a class
QuidditchPlayer
that inherits fromStudent
(it is a child class). This let's you do something likeSince a
QuidditchPlayer
is aStudent
, I can freely uses the methods and attributes ofStudent
from aQuidditchPlayer
instance.Classes and object-oriented programming (OOP) is a huge topic of software engineering. You can read more in probably any self-respecting introduction to Python, with topic such as "but Ron is both a quidditch player and a member of the club S.P.E.W., how do I model this ?", and "what happens a method in a child class have the same name as in the parent ?" Interestingly, sometime the answers to those questions are "Don't use inheritance" or even "Don't use classes"; this highlights the fact that classes and OOP are not the end-all nor the bleeding edge of computer science. But very often, their are an invaluable tool that lets you think more simply about your code.
(note: if this was a more serious piece of code, I would have used some more advanced feature of Python like type annotation (comments-like code that let you text editor help/auto-suggest some code) and f-strings (a way to create strings without all those
+
); but those things can distract from the core understanding of what a class is in Python)Just search for any basic explanation of object-oriented programming. Basically, a class is a prototype (a kind of recipe) for an object, and an object is a collection of state and related behavior.
Everything in a Python program is one of two things, either an object or a class. An object is a concrete concept, like the number 5, the text “Hello, world!”, or a boolean (True/False). A class is an abstract concept. In the examples before the idea of a number rather than 5, or a piece of text rather than specifically “Hello, world!” would be the classes that back those objects.
Each object has a class behind it. You can expose this information using the type() function on a variable name or value. Try printing these out in Python to expose this level of your code. type(5) should reveal that Python calls numbers “int”s. If it’s fractional they are called “float”.
The examples I’ve mentioned are some of the core classes to Python. There are provided to you by the language and the language could not work without them. Each of these classes comes with predefined behavior. That’s how Python knows what to do when you add numbers together. The class of the numbers provides a definition for the result of this interaction.
You can also define extra classes for more specific purposes. What kind of things have you done with Python so far? It’ll help me explain how you might define your own classes in context.
That's not technically true. ints, bools, floats, and strings are primitives and not class based. Ints, bool, and floats, for instance, are stack allocated values (and thus pass by value) while all objects are heap allocated and are pass by reference.
What you're really describing is Python's type system.
I’m lying just a bit for the sake of education. Getting mired in technicalities isn’t good for introducing new concepts.
Ehh, I guess, but I don't think it's that useful of a lie. Intro to classes should probably be more about encapsulation, inheritance, and the rest of the OO pillars. That paragraph was really more about types.
And while there's a decent amount of overlap there, since python is a language before everyone started to adopt ML types, so objects are the primary way for users to create their own types (although there's also functions and closures!), but it seems to just be creating misunderstandings down the road unnecessarily.
I would argue that those require an existing understanding of what a basic class is. I wouldn’t introduce those to someone that only just heard of classes as a concept for the first time.
I have a formal CS education. I was introduced to OOP in a similar way to what you’re suggesting. I don’t think it’s the best way to teach but I’d love to go through some teaching trial and error to discover what is a better way.
Lying for the sake of education is never useful. You can omit things but then your omissions have to let the rest make sense standalone.
"Everything is an object" is a more common idiom in python and one that is easier to understand tbh
If that's the case, why do people say everything in Python behave like an object and there are no true primitives (as opposed to something like Java which has both)?
How do ints have methods associated with them, like bit_length() if they aren't objects?
I'm pretty sure. Just as a matter of practicality it would be ridiculous to use two ints, a stack allocation, and a heap allocation for every integer. They may have duck typed methods but at least CPython does not handle them the same as user created classes, which are all heap allocated.
According to this article, even single-digit (single-word) CPython integers use 28 bytes (plus the reference pointer).
In true single-kinded languages like Python, I think it's simpler to think of integers etc. as "immutable" rather than "pass-by-value".
Everything is an object in Python, including classes and types, and their types, too.
I know. I figured I halt the depth of the explanation there because otherwise it's cyclical.
A function is what you make when you want to reuse a snippet of code. This let's you stuff all the applicable crap into a tidy little one-liner that you can call over and over and over again.
But what if you have functions that take a ton of inputs? Or especially, what if you have a set of related functions that all take the same big group of inputs?
Think of a mortgage program. You could have a mortgage program use a function called TotalInterestPaid that calculates the total amount of interest you pay for the course of the loan. It would need the principle amount, interest rate, and length of the loan to calculate the total interest payment.
Then suppose you want to calculate the monthly payments. Again, you need all the same inputs: principle, interest, length of loan. Then total payoff amount requires the same inputs again and how many months you have left on the loan.
There are a couple keys to realize a class is suitable: (1) as you write the stuff you want, you realize you're referring to the same thing (object) repeatedly - the mortgage interest, the mortgage payoff, etc. (2) you realize you're passing the same bundle of arguments together to various functions - the values belong together as a group.
So what you can do is make a class to hold all of these things together for you. A mortgage class could have an interest rate, a principle amount, a length in months, and a length of time remaining in months. You can use a class constructor to force the user to supply values for the arguments - can you really have a mortgage with no principle? Etc.
Then, finally, the class can have functions that get "free" access to any of the class members, so when you have a function like GetPayoffAmount, it can return the dollar amount but doesn't require any arguments, because you already set the values on construction and those values live with that particular instance of the class.
Classes can do more, like inheritance, but that's the general gist of a class - they let you bundle the individual variables together that you keep having to pass as a group, and provide you an easy single-variable way to access that similar group of functionalities in a kind of similar way to how a function gives you a single-line way to access a sequence of operations.
So like others said classes aren't unique to Python, they're an object-oriented thing.
Basically, classes (like others have said) are blueprints for objects.
Objects have attributes and methods.
Take a car. A car has attributes (what color it is, how fast it can go, it's fuel efficiency) and methods (braking, accelerating, measuring speed, etc.)
A class describes these methods and attributes.
From there you can make instances of objects. For example, you can have your factory create a car called a Coupe, that's red, with a top speed of 150mph, and it can do all the same things other "car" objects can do (it has the same methods as described in the class blueprint).
If you've done anything with the Turtle module it's really helpful for understanding this concept. You create Turtles (or more accurately instances of the Turtle class/Turtle object) with a name and color, and you tell each of them separately to do stuff that they can all do (move, turn left, turn right).
Classes and the object-oriented paradigm (in Java and Python) are probably the most confusing programming concepts to teach or learn because of how abstract they are and how early they're typically introduced. While I'm also going to use the physical-things-and-blueprints metaphors you'll see very often, just remember you're really just working with code.
A class, at its core, is a way to reuse code. They're very similar to functions in that regard - but instead of providing a series of steps to take, a class is more like a file - bundling together some functions, variables, and other code. They don't do anything on their own, just like functions. This is where the "blueprint" or "DNA" analogy is helpful - creating objects is necessary to actually do anything with classes.
Objects are a result of instanciation - which you're probably already familiar with from variables. Just as a variable
a = "this is a string"
isn't the same object asb = "this is a string"
(even if their contents are equal, in most languages ) instances of classes aren't the same even if their contents are equal, and their state can change and diverge just like variables can (b = "this is still a string"
,b != a
). Multiple turtles / objects can be created from turtle DNA / a Turtle class, and while starting out the same, can diverge with changing attributes.Inheritance comes from the idea of what if you make a class, based on an already existing class? It's functionally very similar to if you copy-and-pasted the existing code for a class inside your new one - you can change functions or internal structure at your discretion, and get all the benefits of code reuse. A SoftShelledTurtle, for example, inherits the structure of a Turtle, but changes parts of it - like the composition of its shell.
You might have already seen some examples of encapsulation. It's less a fundamental concept and more a best-practice kind of thing. While you can directly modify variables and internal states of objects, that's sometimes a bad idea - especially when working in shared codebases, someone who doesn't fully understand your code might accidentally make your turtle object go haywire! Encapsulation aims to avoid this by only letting the contents of a class be changed by certain exposed functions. So instead of changing a turtle's age by finding and directly changing the age variable
turtle.age = 18
, using a pre-made functionturtle.setAge(18)
accomplishes the same thing, sometimes safer.These explanations here and elsewhere won't likely provide you with more than a general understanding of what you're working with. It really just takes some practice to wrap your head around object oriented fundamentals, especially the ideas of classes, objects, and inheritance. I learned object-oriented programming by writing a lot of code that closely represented physical hardware, and unfortunately don't remember any online tutorials that stuck out to me - but I'd advise you to design and write your own systems alongside whatever tutorial you end up going with.
To give a perspective that comes from a more functional-programming lense: Classes bundle a data structure with behaviour that makes sense on it. Ever find yourself throwing around the same 3-tuple of values all the time? Many languages (not python*) would wrap that in a struct (a struct is basically a tuple that has its own type where every place has a name.). You have a set of standard operations that you do on a struct that possibly change the struct? You might want to bundle that data + function combination into a class. This changes an expression such as "a = func(a,b)" to "a.func(b)". Similarly, "(a,b) = func(a,c)" changes into "b = a.func(c)".
If that is useful to making your code understandable, you've got yourself a class.
* A python class without any function definitions is basically a struct. A class is just a struct with functions.
ETA: I should note that there's the more advanced OOP concepts such as inheritance. Don't worry about that now, first get familiar with classes. But do note that if you find yourself having commonality between several classes, that inheritance is a thing and can help you here; for example if several different classes all share a core set of functions that do similar things, maybe in slightly different ways.
This video is one of the best explanations I've encountered on the topic:
Python OOP Tutorial 1: Classes and Instances
There's a reason Corey Schafer's material is so well known, he talks to you like a human being and, in plain language, explains concepts. Please watch this video and let me know if it helped you!