##  [Getting started with classes in Python Part 5: `classmethod` and `staticmethod`](/blog/getting-started-classes-python-part-5-classmethod-and-staticmethod) 

Methods on classes have to include the `self` parameter to work properly, however, there are two notable exceptions to this rule that are worth learning about. They are `classmethod`s and `staticmethod`s.

## `staticmethod`

A method is said to be "static" when it doesn't use the class properties at all. In other words, "`self`" is unused within the method body. However, you can't then remove the parameter `self` from the definition because it *has* to be there for methods to work. Unless, that is, you use the decorator `staticmethod` on the method. If that is applied you can remove the `self` parameter and still have other non-`self` parameters. Static methods like this can still be called using `self` within other methods, but they can also be called via the class and instances of the class. Why would you ever want to do this though? It crops up surprisingly often, but generally you may want to group utility functions together to make them easier to use.

For example, consider the `Coordinate` class from Part 4. A static method is generally a method or function that is strongly associated with the class, even if it doesn't use the class properties.

```Python
from dataclasses import dataclass


@dataclass(frozen=True)
class Coordinate:
    x: int
    y: int
    z: int


```

For `Coordinate` we might want to create a utility function to get possible values of `x`, `y` and `z`. Let's fix the minimum and maximum values of the three dimensions as constants at the beginning of the file. Now we can call them in the class. Our `staticmethod` would look something like this.

```Python
from dataclasses import dataclass


MIN_X, MAX_X = (0, 10)
MIN_Y, MAX_Y = (0, 10)
MIN_Z, MAX_Z = (0, 10)


@dataclass(frozen=True)
class Coordinate:
    x: int
    y: int
    z: int

    @staticmethod
    def get_possible_values(axis: str):
        axis = axis.lower()
        if axis == 'x':
            min_, max_ = MIN_X, MAX_X
        elif axis == 'y':
            min_, max_ = MIN_Y, MAX_Y
        elif axis == 'z':
            min_, max_ = MIN_Z, MAX_Z
        else:
            raise ValueError(f'axis {axis} is invalid. Choose "x", "y", or "z".') 
        return [i for i in range(min_, max_+1)]


# The staticmethod can be called from both the class and instances of the class!
print(Coordinate.get_possible_values('x'))
print(Coordinate(0, 0, 0).get_possible_values('x'))

```

Thus we can write a class method that does not use `self`. If you still aren't convinced by this tool's efficacy don't worry. Once you've created your own classes and developed your own software using them, you **will** come across situations like this and this is a tool for those times.

## `classmethod`

Class methods are just as easy to create and apply as static methods, but they have a very different purpose. Class methods are *instance creation methods*. This is easier to explain with an example. To do so, let's consider the `ThreeDimensionalSpace` class from Part 4.

```python
@dataclass
class ThreeDimensionalSpace:
    coordinates: set[Coordinate]

```

I have omitted the other methods as we don't need them for the example.

When working in 3D cartesian space (i.e. in some sort of 'box') we need to create instances of `Coordinate` for every point, but all we need to know to do this is really the minimum and maximum values for x, y, and z. Since x, y, and z must be integers we can work out every possible coordinate from just 6 numbers: min and max of x, y, and z.

However, the class `ThreeDimensionalSpace` needs us to create the set of all these coordinates *every time* before we can instantiate it. What if we built it in?

```python
@dataclass
class ThreeDimensionalSpace:
    coordinates: set[Coordinate]

    @classmethod
    def from_min_max(cls, xlims: tuple[int, int], ylims: tuple[int, int], zlims: tuple[int, int]):
        coordinates = set()
        for x in range(xlims[0], xlims[1]+1):
            for y in range(ylims[0], ylims[1]+1):
                for z in range(zlims[0], zlims[1]+1):
                    coordinates.add(Coordinate(x, y, z))
        return cls(coordinates)

```

Enter the `classmethod`. This is a method that can be called from the class itself, or an instance of a class like `staticmethod`, and its selling point is that it creates a new instance of the class. The first parameter for a method decorated with `classmethod` must always be `cls`, which is the constructor of the class. In practice this lets you add class-creation shortcuts to your work. It can even be used as the *only* way to instantiate the class, which can be useful when you don't want users to construct the class themselves in the "normal" way or perhaps there is a private way of instantiating the class that you don't want users to see.

```python-repl
In [12]: new = ThreeDimensionalSpace.from_min_max((0, 2), (0, 2), (0, 2))

In [13]: for c in new.coordinates:
    ...:     print(c)
    ...:
Coordinate(x=2, y=0, z=2)
Coordinate(x=0, y=1, z=0)
Coordinate(x=2, y=2, z=2)
Coordinate(x=2, y=1, z=0)
Coordinate(x=1, y=2, z=2)
Coordinate(x=0, y=0, z=1)
Coordinate(x=0, y=2, z=1)
Coordinate(x=1, y=0, z=1)
Coordinate(x=1, y=1, z=0)
Coordinate(x=2, y=0, z=1)
Coordinate(x=2, y=1, z=2)
Coordinate(x=2, y=2, z=1)
Coordinate(x=0, y=1, z=2)
Coordinate(x=1, y=2, z=1)
Coordinate(x=0, y=2, z=0)
Coordinate(x=0, y=0, z=0)
Coordinate(x=1, y=1, z=2)
Coordinate(x=1, y=0, z=0)
Coordinate(x=2, y=0, z=0)
Coordinate(x=2, y=2, z=0)
Coordinate(x=0, y=1, z=1)
Coordinate(x=2, y=1, z=1)
Coordinate(x=1, y=2, z=0)
Coordinate(x=0, y=0, z=2)
Coordinate(x=0, y=2, z=2)
Coordinate(x=1, y=0, z=2)
Coordinate(x=1, y=1, z=1)

```

Hopefully the utility of class methods should be self-evident. However both of these decorators are simply useful tools that you should know about. In this part I have covered static and class methods, and I briefly mentioned the concept of "Private" methods and classes, which will be the subject of the next installment of this series. Stay tuned for Part 6: Public, Private, and Super Private Naming.

For a complete look at the articles in this series, have a look at this overview page: [Getting started with classes in Python](https://developer.ansys.com/blog/getting-started-classes-python)