91

I'm a Java developer who's toyed around with Python on and off. I recently stumbled upon this article which mentions common mistakes Java programmers make when they pick up Python. The first one caught my eye:

A static method in Java does not translate to a Python classmethod. Oh sure, it results in more or less the same effect, but the goal of a classmethod is actually to do something that's usually not even possible in Java (like inheriting a non-default constructor). The idiomatic translation of a Java static method is usually a module-level function, not a classmethod or staticmethod. (And static final fields should translate to module-level constants.)

This isn't much of a performance issue, but a Python programmer who has to work with Java-idiom code like this will be rather irritated by typing Foo.Foo.someMethod when it should just be Foo.someFunction. But do note that calling a classmethod involves an additional memory allocation that calling a staticmethod or function does not.

Oh, and all those Foo.Bar.Baz attribute chains don't come for free, either. In Java, those dotted names are looked up by the compiler, so at runtime it really doesn't matter how many of them you have. In Python, the lookups occur at runtime, so each dot counts. (Remember that in Python, "Flat is better than nested", although it's more related to "Readability counts" and "Simple is better than complex," than to being about performance.)

I found this a bit strange because the documentation for staticmethod says:

Static methods in Python are similar to those found in Java or C++. Also see classmethod() for a variant that is useful for creating alternate class constructors.

Even more puzzling is that this code:

class A:
    def foo(x):
        print(x)
A.foo(5)

Fails as expected in Python 2.7.3 but works fine in 3.2.3 (although you can't call the method on an instance of A, only on the class.)

So there's three ways to implement static methods (four if you count using classmethod), each with subtle differences, one of them seemingly undocumented. This seems at odds with Python's mantra of There should be one-- and preferably only one --obvious way to do it. Which idiom is the most Pythonic? What are the pros and cons of each?

Here's what I understand so far:

Module function:

  • Avoids the Foo.Foo.f() problem
  • Pollutes the module's namespace more than the alternatives
  • No inheritance

staticmethod:

  • Keeps functions related to the class inside the class and out of the module namespace.
  • Allows calling the function on instances of the class.
  • Subclasses can override the method.

classmethod:

  • Identical to staticmethod, but also passes the class as the first argument.

Regular method (Python 3 only):

  • Identical to staticmethod, but can't call the method on instances of the class.

Am I overthinking this? Is this a non-issue? Please help!

Doval
  • 2,516
  • 1
  • 22
  • 22

4 Answers4

122

The most straightforward way to think about it is to think in terms of what type of object the method needs in order to do its work. If your method needs access to an instance, make it a regular method. If it needs access to the class, make it a classmethod. If it doesn't need access to the class or the instance, make it a function. There is rarely a need to make something a staticmethod, but if you find you want a function to be "grouped" with a class (e.g., so it can be overridden) even though it doesn't need access to the class, I guess you could make it a staticmethod.

I would add that putting functions at the module level doesn't "pollute" the namespace. If the functions are meant to be used, they're not polluting the namespace, they're using it just as it should be used. Functions are legitimate objects in a module, just like classes or anything else. There's no reason to hide a function in a class if it doesn't have any reason to be there.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • When I wrote about polluting the namespace, I was thinking more along the lines of more potential name clashes if someone were to do "from Foo import *". I suppose it's each programmer's responsibility to watch out for that though. – Doval Aug 03 '12 at 02:06
  • 19
    That's why `from Foo import *` is discouraged. If the functions are meant to be internal to the module and not part of the public API, you can name them with a leading underscore and they won't be imported when you do `from Foo import *`. – BrenBarn Aug 03 '12 at 02:07
  • 5
    +1 for `If the functions are meant to be used, they're not polluting the namespace, they're using it just as it should be used.` If that's polluting, then why have a namespace at all if you're just going to avoid using it? – rickcnagy Jul 17 '14 at 12:42
  • great overview of the differences between these decorators here: https://stackoverflow.com/a/1669524/1080804 – ecoe Sep 09 '18 at 19:07
45

Great answer by BrenBarn, but I would change 'If it doesn't need access to the class or the instance, make it a function' to:

'If it doesn't need access to the class or the instance...but is thematically related to the class (typical example: helper functions and conversion functions used by other class methods or used by alternate constructors), then use staticmethod

else make it a module function

Community
  • 1
  • 1
smci
  • 32,567
  • 20
  • 113
  • 146
  • Well, ``make_from_XXX()`` sounds like an alternate constructor so it doesn't fit properly as a staticmethod. – Gall Jul 03 '15 at 08:09
  • @Gall you're correct, I changed that comment. There is a good discussion at [Using static methods in python - best practice](http://stackoverflow.com/questions/15017734/using-static-methods-in-python-best-practice) – smci Jul 03 '15 at 13:23
  • 1
    Static method is really just a cosmetic thing. There is practically no difference between a staticmethod and a function, as staticmethod gets all its info from args alone, just like plain funcs. You can take any func and stick it onto a class as a static, or vice versa, and it wouldn't change absolutely anything apart from the way you invoke them. –  Oct 16 '16 at 10:39
  • 1
    @hayavuk: there's a conceptual difference in that we signal that a staticmethod 'belongs' to a particular class, and its code, docstring, documentation, related constants etc. should be kept together, in the same files. If we didn't do that, consider e.g. the [`datetime`](https://docs.python.org/2/library/datetime.html) module: there would be less hint that `datetime.fromtimestamp()` is not supposed to be called on any old integer. – smci Oct 17 '16 at 23:02
  • 1
    @smci I would still file "we signal" as a cosmetic thing. It doesn't really change the way `fromtimestamp()` behaves. Tho I think `fromtimestamp()` should really be a classmethod here, not a staticmethod. –  Oct 23 '16 at 19:24
  • @hayavuk In a large module or even a large file, keeping related code (/doc/constants) together and unrelated code apart becomes non-trivial. – smci Oct 23 '16 at 22:01
  • @smci How does that disprove the fact that there's technically little difference between a function and a staticmethod? –  Oct 24 '16 at 13:19
  • @hayavuk: We're not talking about semantics down at the compiler level. We're talking about at the project, directory, and file level, it is really unwise to create an unmanageable jumble of 1000s of methods, helper functions and constants. If you've ever had the misfortune to have to join such a project late on (I have), you'll know that. – smci Oct 03 '17 at 20:30
16

This is not really an answer, but rather a lengthy comment:

Even more puzzling is that this code:

        class A:
            def foo(x):
                print(x)
        A.foo(5)

Fails as expected in Python 2.7.3 but works fine in 3.2.3 (although you can't call the method on an instance of A, only on the class.)

I'll try to explain what happens here.

This is, strictly speaking, an abuse of the "normal" instance method protocol.

What you define here is a method, but with the first (and only) parameter not named self, but x. Of course you can call the method in an instance of A, but you'll have to call it like this:

A().foo()

or

a = A()
a.foo()

so the instance is given to the function as first argument.

The possibility to call regular methods via the class has always been there and works by

a = A()
A.foo(a)

Here, as you call the method of the class rather than on the instance, it doesn't get its first parameter given automaticvally, but you'll have to provide it.

As long as this is an instance of A, everything is ok. Giving it something else is IMO an abuse of the protocol, and thus the difference between Py2 and Py3:

In Py2, A.foo gets transformed to an unbound method and thus requires its first argument be an instance of the class it "lives" in. Calling it with something else will fail.

In Py3, this check has been dropped and A.foo is just the original function object. So you can call it with everything as first argument, but I wouldn't do it. The first parameter of a method should always be named self and have the semantics of self.

glglgl
  • 89,107
  • 13
  • 149
  • 217
0

The best answer depends on how the function is going to be used. In my case, I write application packages that will be used in Jupyter notebooks. My main goal is to make things easy for the user.

The main advantage of function definitions is that the user can import their defining file using the "as" keyword. This allows the user to call the functions in the same way that they would call a function in numpy or matplotlib.

One of the disadvantages of Python is that names cannot be protected against further assignment. However, if "import numpy as np" appears at the top of the notebook, it's a strong hint that "np" should not be used as a common variable name. You can accomplish the same thing with class names, obviously, but user familiarity counts for a lot.

Inside the packages, however, I prefer to use static methods. My software architecture is object oriented, and I write with Eclipse, which I use for multiple target languages. It's convenient to open the source file and see the class definition at the top level, method definitions indented one level, and so on. The audience for the code at this level is mainly other analysts and developers, so it's better to avoid language-specific idioms.

I don't have a lot of confidence in Python namespace management, especially when using design patterns where (say) an object passes a reference to itself so that the called object can call a method defined on the caller. So I try not to force it too far. I use a lot of fully qualified names and explicit instance variables (with self) where in other languages I could count on the interpreter or the compiler managing the scope more closely. It's easier to do this with classes and static methods, which is why I think they are the better choice for complex packages where abstraction and information hiding are most useful.

user5920660
  • 129
  • 5
  • Good point. Also within Jupyter it is more convenient to define a separate function; especially if you want to add some examples or tests for it (in particular if using `nbdev`) – Neil Apr 03 '21 at 01:48