Skip to content

Instantly share code, notes, and snippets.

@esoergel
Last active May 24, 2023 12:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save esoergel/544c938d8ffdfcaba82a to your computer and use it in GitHub Desktop.
Save esoergel/544c938d8ffdfcaba82a to your computer and use it in GitHub Desktop.
Brief explanation and example of python's `staticmethod` decorator

You ever factor something out of a method and realize you can get rid of the dependency on self (which is really nice to do for clarity and testability)?

In my experience, typically this is then pulled in to a function, but then you have to move the definition all the way outside of the class, which can suck.

To compensate for this, sometimes you'll leave it as a method and just not use self in the body. This is a good use case for the @staticmethod decorator, which I'll explain:

  • With no decorator, something defined in a class is a method, which requires self (an instance of the class) and a variable number of arguments and keyword arguments.
  • The @property decorator gives you access to JUST self, and lets the result be treated like an attribute of the instance.
  • The @classmethod decorator gives you access to the uninstantiated class, but still has some coupling with the class itself.
  • The @staticmethod decorator basically lets you define a normal function within the context of a class. It has no access to the class or the instance - just to whatever is passed in. Here's an example:
# Without staticmethods:

def count_fruit(apples, bananas):
    # complicated calculation
    return apples + bananas

class Fruit(object):
    def __init__(self, apples, bananas):
        self.apples = apples
        self.bananas = bananas

    @property
    def total(self):
        return count_fruit(self.apples, self.bananas)

>>> fruit = Fruit(3, 5)
>>> fruit.total
8
# the complicated function is easy to test
>>> count_fruit(apples=7, bananas=9)


# With staticmethods:

class Fruit(object):
    def __init__(self, apples, bananas):
        self.apples = apples
        self.bananas = bananas

    @staticmethod
    def count_fruit(apples, bananas):
        # complicated calculation
        return apples + bananas

    @property
    def total(self):
        return self.count_fruit(self.apples, self.bananas)

>>> fruit = Fruit(3, 5)
>>> fruit.total
8
# You can still access the complicated function without needing to instantiate
# the class, which can sometimes be hard (looking at you, Django views/models)
>>> Fruit.count_fruit(apples=7, bananas=9)

This is really nice to get the advantages of functions over methods (clear ins and outs), while still keeping the logical code organization of having the thing associated with the class.

@benrudolph
Copy link

if count_fruit is a static method that means we can still call it with self? like in the example here:

@property
    def total(self):
        return self.count_fruit(self.apples, self.bananas)

(as opposed to Fruit.count_fruit)

@esoergel
Copy link
Author

esoergel commented Sep 8, 2015

Yeah, it behaves like any other class-defined attributes in that way. self is just an instantiated version of the class, so it still has access to all the stuff the uninstantiated class does.

class Fruit(object):
    best_fruit = "watermelon"

    @classmethod
    def class_best_fruit(cls):
        return cls.best_fruit

    def instance_best_fruit(self):
        return self.best_fruit

You can access best_fruit and class_best_fruit (and any staticmethods you've defined) from either an instance of the class or the class itself, but instance_best_fruit can only be accessed from an instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment