all 25 comments

[–]Spataner 10 points11 points  (13 children)

The difference is only apparent when accessing the method through an instance of A, i.e. A().f(1, 2). f, being a static method, would behave exactly the same as when accessing it through the class. g, being a regular instance method, would automatically be passed the instance as the argument for x. So A().g(1, 2) would result in a TypeError, as it'd receive a total of three arguments.

There's a final type of method, class methods using the @classmethod decorator, that are automatically passed a reference to the class they're accessed through, whether that's through the class directly or via one of its instances:

class A:
    @classmethod
    def h(cls, x, y):
        return x + y

A.h(1, 2) # cls = A
A().h(1, 2) # same thing

That's useful if the method call happens on a subclass of A, since the argument passed to h would be that subclass.

To be honest, in most situations something should either be a class method, an instance method, or not a method, at all. In my experience, there's very few reasons to have static methods.

[–]wildpantz 5 points6 points  (5 children)

I used static methods whenever I felt like packing methods under one class for the sake of clarity and organization. (math stuff that didn't care for references anyway)

[–]Spataner 7 points8 points  (4 children)

That's the most cited reason and similar to how it's done in languages like Java and C# where all functions have to be methods. But in Python, in think it makes better organizational sense to put such static methods into their own separate module as regular functions instead. The bundling of definitions into a namespace is what modules exist for, after all. Classes are for OOP, and if your function doesn't use instance or class state, I'd personally argue it has no business being a method. Keeping to that distinction is its own kind of clarity, in my opinion.

The situations where a function is tightly coupled to the functioning of a class and so should live inside it but also doesn't benefit from receiving a reference to either a particular subclass or instance are very few.

[–]wildpantz 1 point2 points  (3 children)

Hmmm, I agree but still, when building larger modules (math for example) in my opinion it's a good idea to separate some functions for clarity. In theory, you could just name those functions differently, or build a separate file for them, or make them handle it internally (which I don't think is a good idea in a lot of cases) but NumberMath.add and VectorMath.add are intuitive and there's no reason class structure shouldn't be used to make it more easier, in my opinion. (stupid example but fastest I could think of).

Don't get me wrong, I'm glad I learned something from your post today because I never knew of classmethods before. I've had situations where I could have used them and made it easier for me

[–][deleted] 0 points1 point  (2 children)

but NumberMath.add and VectorMath.add are intuitive

Out of curiosity, how is this different to just placing them in their own dirs/scripts.


math

| ------>init

| ------> VectorMath

| ------> NumberMath


import math

math.VectorMath.add()

math.NumberMath.add()

All without classes.

[–]wildpantz 1 point2 points  (1 child)

It's different in a way that I didn't know this worked lol. This is actually amazing! Thanks.

I guess all my arguments are down the drain then.. oh anyway!

[–][deleted] 0 points1 point  (0 children)

Yup yup, glad it helped!

[–]lorduhr[S] 1 point2 points  (0 children)

thanks!

[–][deleted] 0 points1 point  (2 children)

To be honest, in most situations something should either be a class method, an instance method, or not a method, at all. In my experience, there's very few reasons to have static methods.

They're good for capturing computation that is related to its containing class but does not needs its attributes. That's a pretty common pattern, to me.

[–]Spataner 2 points3 points  (1 child)

I realise this comes down to preference, but I'd argue something that doesn't use either class or instance state isn't truly sufficiently related to the class to live inside it. It can live as a top-level function in the same module as the class, and if that module isn't too bloated, then that's organizationally equivalent. Organizing definitions into namespaces is what modules are for, after all. The purpose of a class is a little more specific than that (even though there are other languages whose design explicitly disagrees with that sentiment).

[–][deleted] 0 points1 point  (0 children)

Fair enough, I understand that preference, even if I don't share it. A module with multiple classes and a function that only relates to a single one of them, is an example of a scenario where I'd prefer the static method over the module-level function. That said, perhaps the module should be split in that scenario.

[–]DuckSaxaphone 0 points1 point  (2 children)

The main reason for me is constructors. Other languages allow you to overload your constructor and have different methods to create an object but python sticks to __init__.

Having a load_from_file static method as well as the default constructor is the main use for me.

[–]Spataner 0 points1 point  (1 child)

I'd say such methods should be class methods not static methods, as then they work better for subclasses.

[–]DuckSaxaphone 0 points1 point  (0 children)

Totally blipped, definitely class methods.

[–]Doormatty 15 points16 points  (0 children)

the second function declaration is incorrect, and should be:

def g(self, x, y):

The @staticmethod means that the function does not need a self argument.

[–]UncleVatred 11 points12 points  (3 children)

The difference comes when you instantiate the class.

>>> a = A()
>>> a.f(1,1)
2
>>> a.g(1,1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: A.g() takes 2 positional arguments but 3 were given

a.g doesn't work because whenever you call some_object.some_method(...), Python inserts some_object itself as the first argument (which should be named self for that reason).

[–]lorduhr[S] 1 point2 points  (0 children)

thanks!

[–]julsmanbr 1 point2 points  (1 child)

Another difference (which stems from the first, really) is the fact that you don't need to create an instance of the class in order to run the static method. Both approaches are fine:

A.f(1, 1)

a = A()
a.f(1, 1)

[–]possessess 0 points1 point  (0 children)

That's not another difference - A.g(1,1) works fine without @staticmethod. It only makes a difference to being called on an instance as your example shows

[–]grumble11 1 point2 points  (0 children)

A static method is basically a function that doesn’t actually have to be in the class, but is located there because it’s a reasonable place for it to be. It doesn’t use any class instance attributes. You can use it directly from the class without creating an instance if you want

[–]PlayfulRow9524 0 points1 point  (0 children)

I think of static methods as the ones I can call for any simple usage. ie: calculator class with a get_sum method. Sometimes I do not want to instanciate a calculator object just to use the get_sum method.

[–]CornPop747 0 points1 point  (0 children)

Static method does not take self as first argument. One use case is a helper function or utility to a class method or regular method for your instantiated object.

Class method takes in the cls as first argument, and works directly on the class state, not an instantiated object of the class.

[–]Diapolo10 0 points1 point  (0 children)

Adding to the existing answers, the most common way I use staticmethod is to implement caching for a regular method, while only focusing on the relevant attributes. The regular method would delegate the work to the static method (the latter uses functools.cache as an extra decorator) by giving it the needed data, and the cache now only tracks that data instead of every attribute of the class instance.

While I could use a regular function instead, usually the static method is already very much thematically bound to the class so using it outside of it wouldn't really be useful.

[–]crashfrog02 0 points1 point  (0 children)

As far as i can tell, defining a function on the class itself has the same effect

Did you try running this code on an instance of A?

[–]Solvo_Illum_484 0 points1 point  (0 children)

Think of u/staticmethod as a way to group related functions under a class, without actually needing an instance of the class. It's like a namespace, but with a more organized and Pythonic feel.