HanaKaze
HanaKaze

Reputation: 209

Why choose module level function over @staticmethod in Python (according to Google Style Guide)?

According to Google Python Style Guide, static methods should (almost) never be used:

Never use @staticmethod unless forced to in order to integrate with an API defined in an existing library. Write a module level function instead

What are the reasons behind such recommendation?

Is this particular to Google only or are there any other (more general) downsides with using static methods in Python?

Especially, what is the best practice if I want to implement an utility function inside of a class that will be called by other public member functions of that class?

class Foo: 
    .......
    def member_func(self): 
        some_utility_function(self.member)

google python style guide

Upvotes: 7

Views: 3459

Answers (3)

keepAlive
keepAlive

Reputation: 6655

My 2¢

The point is that when you want to do duck-typing polymorphic things, defining module level functions is overkilled, especially if your definitions are very short. E.g. defining

class StaticClassA:
    @staticmethod
    def maker(i: int) -> int:
        return 2*i

class StaticClassB:
    @staticmethod
    def maker(i: int) -> float:
        return pow(i, 2)

#[...] say, 23 other classes definitions

class StaticClassZ:
    @staticmethod
    def maker(i: int) -> float:
        return 2*pow(i, 2)

Is clearly smarter than having 26 (from A to Z) classes defined within 26 modules.


A practical example of what I imply with the word "polymorphism" ? With the above classes definitions, you can do

for class_ in [StaticClassA, StaticClassB, StaticClassZ]:
    print(class_.maker(6))

Upvotes: 0

abarnert
abarnert

Reputation: 365617

How to understand the Google Python Style Guide that says:

Never use @staticmethod unless forced to in order to integrate with an API defined in an existing library. Write a module level function instead

Well, you should understand it as Google's style guide. If you're writing Python code for Google, or contributing to a project that conforms to that style guide, or have chosen to use it for a project of your own, the answer is pretty simple: Don't use @staticmethod except when forced to by an API.

This means there are no judgment-call cases: A utility function inside of a class is not forced to be a @staticmethod by an API, so it should not be a @staticmethod.

The same is true for some other common1 reasons for @staticmethod. If you want a default value for an instance attribute that's meant to hold a callback function… too bad, find another way to write it (e.g., a local function defined inside __init__). If you want something that looks like a @classmethod but explicitly doesn't covary with subclasses… too bad, it just can't look like a @classmethod.


Of course if you're not following Google's style guide, then you should understand it as just one opinion among many. Plenty of Python developers aren't quite as hard against @staticmethod as that guide is. Of course Google is a pretty prominent developer of lots of Python code. On the other hand, Google's style guide was written while imported Java-isms were more of a problem than today.2 But you probably don't want to think too much about how much weight to give each opinion; instead, when it's important, learn the issues and come up with your own opinion.


As for your specific example, as I said in a comment: the fact that you naturally find yourself writing some_utility_function(self.member) instead of self.some_utility_function(self.member) or Foo.some_utility_function(self.member) means that intuitively, you're already thinking of it as a function, not a @staticmethod. In which case you should definitely write that one as a function, not a @staticmethod.

That may be just the opinion of one guy on the internet, but I think most Python developers would agree in this case. It's the times when you do naturally find yourself prefixing self. before every call when there's a judgment call to make.


1. Well, not exactly common. But they aren't so rare that they never come up. And they were common enough that, when there was discussion about deprecating @staticmethod for Python 3, someone quickly came up with these two cases, with examples from the standard library, and that was enough for Guido to kill the discussion.

2. In Java, there are no module-level functions, and you're forced to write static methods to simulate them. And there were a few years where most university CS programs were focused on Java, and a ton of software was written by Java, so tons of people were writing Python classes with way too many @staticmethods (and getters and setters, and other Java-isms).

Upvotes: 5

kindall
kindall

Reputation: 184081

The way you've written the call to some_utility_function(), it isn't defined on the class anyway. If it were, you would be using self.some_utility_function() or possibly Foo.some_utility_function() to call it. So you've already done it the way the style guide recommends.

The @classmethod and @staticmethod decorators are used primarily to tell Python what to pass as the first argument to the method in place of the usual self: either the type, or nothing at all. But if you're using @staticmethod, and need neither the instance nor its type, should it really be a member of the class at all? That's what they're asking you to consider here: should utility functions be methods of a class, when they are not actually tied to that class in any way? Google says no.

But this is just Google's style guide. They have decided that they want their programmers to prefer module-level functions. Their word is not law. Obviously the designers of Python saw a use for @staticmethod or they wouldn't have implemented it! If you can make a case for having a utility function attached to a class, feel free to use it.

Upvotes: 2

Related Questions