Typefully

Object-Oriented Programming Series: 7. Final Year (Class Methods and Static Methods)

Avatar

Share

 • 

3 years ago

 • 

View on X

Object-Oriented Python at the Hogwarts School of Codecraft and Algorithmancy --- Year 7: Final Year (Class Methods | Static Methods) --- Students are taller than most professors now–what a change from Year 1! But there's still one Year to go before graduating from Hogwarts
Yes, that code snippet above is long - it's a summary of all previous years' work, after all! Here's a link back to last Year, Year 6: twitter.com/s_gruppetta_ct/status/1644010840469958661 And if you want to start from the beginning, here's Year 1: twitter.com/s_gruppetta_ct/status/1641121936414736385
There's one # ToDo comment in there. It's in `Student.assign_house_using_sorting_hat()` Let's go through a few versions of this method–not all will be ideal but it will help us see the progression of how we get to a good solution Here's a first (flawed) attempt
I'm showing this example first to highlight an issue we need to think about This method chooses a house at random for a student (Yes, I know, the Sorting Hat wasn't random, but, but…) Let's try it out in `making_magic.​py`
Note that houses are chosen at random. I ran this script several times until Harry and Ron where both assigned to the same house to demonstrate this Note how Harry is the only member of his house even though both he and Ron where assigned to Gryffindor
The objects in `harry.​house` and `ron.​house` are different instances of the class `House`, even though they have the same values for name, founder, and so on This is because new instances for each house are created each time you call `.assign_house_using_sorting_hat()`
Second attempt: let's pass a sequence with all the houses to the method and we can create the houses elsewhere, not within the method itself
We can create the `House` instances in `making_magic.​py`, for now In this run, all 3 students happened to end up in the same house, Slytherin (it's random allocation!) Since there's only one Slytherin instance, they're all in the same instance now, not separate ones
This works, although if you're using these houses in separate scripts within a larger project, you have to be careful to define the houses only once and re-use the same instances across your project… But there's another way
If we are to define the house instances once, we can do so in a class Let's create a new module (a new file) called `hogwarts_class.​py` and define a class called `Hogwarts` This class is a bit different to the ones you've defined so far Let's look at it in more detail
The class has two attributes The first is `_houses`. It is a _class attribute_ Note how it's not linked to `self` as other data attributes we used in previous Years It contains a dictionary linking the names of the houses with instances of `House` for each one
The leading underscore in `_houses` is not linked to this being a class attribute. It's a convention we use to show this is a private attribute and should only be used internally within the class It's just a convention and Python doesn't force this behaviour
This class attribute is not specifically attached to an instance, as the instance variables we've seen before It can be accessed directly from the class, such as `Hogwarts._houses`
However, if you create instances of this class, you can also access this data through the instance But in this example we won't create instances of `Hogwarts`. This is also why we don't need an `__init__()` method!
The second attribute in this class is a method. However, this is not an instance method like the ones we've seen before The `@​classmethod` before the function definition takes care of this You'll notice that the first parameter is not `self` but `cls`
A class method is not attached to an instance and therefore doesn't need `self`, which refers to the instance Instead, a class method is linked to the class and therefore it needs the class as its first parameter
When you call this method, the class is passed implicitly, as you'll see soon This is similar to how `self` is the first parameter in an instance method but you don't need to explicitly add the object in the parentheses when you call the method–it's automatically included
This method accesses `_houses`, which is a dictionary. It uses the dictionary's `.get()` method to get the value associated with the key–this is a `House` object We normally prefer to use `get_house()` to access the `_houses` class attribute instead of accessing it directly
You're only creating one instance of each `House` Therefore, each time you use `Hogwarts.get_house()`, you're fetching one of these four instances
Let's make the necessary changes in `Student` back in `hogwarts_magic.​py` The method now accepts a school to indicate the school—this is the name of a class, such as `Hogwarts` In this example we only have a `Hogwarts` school class, but you could have more schools!
This method now picks a random house name and fetches the instances created within the `Hogwarts` class. There will only be one instance for each house But, have a look at that last code snippet again…
We used `school._houses.keys()` in the `Student` class. We're accessing the private attribute `_houses` from the `Hogwarts` class Python won't stop you from doing this. But it's best to avoid it. So let's add another class method in `Hogwarts` and then use the class method
We can finally update `making_magic.​py` to import the `Hogwarts` class and use it in the calls to `.assign_house_using_sorting_hat()`
Let's have a look at another example with class attributes You need to assign a unique ID to each student—even wizards need bureaucracy, after all! You can create a class attribute with the student count This is an attribute that must belong to the class, not an instance
Each time you create a `Student` instance, the `__init__()` method is called. This: 1. Increments the class attribute `Student.​student_count` – note that there's no `self` used on this line. This is a value that belongs to the class as a whole
2. It creates `self.​id` using the class attribute `.student_count`. The instance data attribute `.id` belongs to the instance—each student has a unique ID You can see this in action here:
Before wrapping up the Year and your entire studies at Hogwarts School of Codecraft and Algorithmancy, there's one more type of method to discuss So far, you've seen instance methods and class methods
Instance methods belong to the instance of a class. The instance is passed to the method as its first argument, normally called `self` Therefore, when you call `harry.​assign_wand(some_wand)` you're effectively calling `Student.​assign_wand(harry, some_wand)`
Class methods have access to the class as a whole instead of individual instances. These have the class passed as their first argument instead of the instance Another type of method is the _static method_ which doesn't have access to the instance or the class
Therefore, static methods do not need a required first parameter such as `self` for instance methods and `cls` for class methods Here's an example of a static method in the `Wand` class
There's `@​staticmethod` before the class definition to indicate what type of method this is Note that this method doesn't need any information that's stored within the class or an instance of the class It's a standalone method but it fits well within the `Wand` class
Here's how we can use this method You can call it directly from the class, as in `Wand.​get_all_wand_makers()` You could call it from an instance, too, but this has no additional information relating to the instance
This brings us to the end of of Year 7 and of your studies at Hogwarts School of Codecraft and Algorithmancy You're now a fully qualified wizard or witch… …and more importantly, hopefully you have a better understanding of classes and OOP than before you started!
This was a long series with long threads Here's the final version of `hogwarts_magic.​py`—note there are some bits still missing in this code (for example, not all classes have `__repr__()` methods—they all should!) That's up to you to finish now! gist.github.com/stephengruppetta/abf8d81d70022c0c2702f6479e772664
I'll turn this into a series of articles on The Python Coding Book soon… …but not yet. I need a break after this series! Plus, the very first live cohorts of The Python Coding Programme start soon More here if interested (very few places left)…thepythoncodingbook.com/2023/03/31/the-python-coding-programme-a-live-course-for-beginners-with-small-cohorts-active-mentoring
Avatar

Stephen Gruppetta

@s_gruppetta_ct

Constantly looking for innovative ways to talk and write about Python • Mentoring learners • Writing about Python • Writing about technical writing