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 JUSTself
, 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.
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.You can access
best_fruit
andclass_best_fruit
(and any staticmethods you've defined) from either an instance of the class or the class itself, butinstance_best_fruit
can only be accessed from an instance.