views:

429

answers:

8

I've been working with Python for a while and I find the syntax for declaring methods as static to be peculiar.

A regular method would be declared:

def mymethod(self, params)
   ...
   return

A static method is declared:

def mystaticethod(params)
   ...
   return
mystaticmethod = staticmethod(mystaticmethod)

If you don't add the static method line, the compiler complains about self missing.

This is a very complex way of doing something very simple that in other languages simply use a keyword and a declaration grammar to. Can anyone tell me about the evolution of this syntax? Is this merely because classes were added into the existing language?

Since I can move the staticmethod line to later in the class, it also suggests that the parser is working extra hard on bookkeeping.

Note that I'm aware of the decorator syntax that was added later, I'm interested to know how the original syntax came about from a language design perspective. The only think I can think of is that the staticmethod application invokes an operation that transforms the function object into a static method.

+2  A: 

Starting in Python 2.4, one can also use a decorator as in:

   @staticmethod
   def mystaticethod(params)
      ...
      return

But I do not have any insight as to the genesis of this feature, as implemented orginially, in the language. But then again, I'm not Dutch :-) Do see Michael E's response in this post, regarding the late arrival of static methods in the evolution of Python.

BTW, for all their simplicity, as

 @MyDeco
 someObject

   is merely "syntactic sugar" for

  MyDeco(someObject)

decorators can be used for many other cool things!

mjv
My understanding is that the decorators only work with 2.4 and up, so they came later because the syntax was like that. I'm still wondering about why the syntax was like that.
Uri
+12  A: 
Michael E
A: 

Perhaps, the design did not initially think static method is needed when function can be used. Since python didn't have data hiding so there is really no need static methods rather than using classes as name spaces.

NawaMan
+1  A: 

Guido writes the blog The History of Python. I think there is a way to contact him with request to expand on this particular topic.

SilentGhost
Wasn't aware of that blog, thanks for the ref!
Uri
+3  A: 

Gido has always been wary of adding new constructs to the language. When static methods were proposed, it was showed that you could already do it (there is a staticmethod() decorator since 2.2), you just didn't have the syntactic sugar for it.

If you read the PEP you can see all the disscussion that goes into adding something. I, for one, like that approach. It reminds me of C in that there are no unnecesary keywords.

When the new syntax for decorators where added to Python 2.4, you could use the existing decorators with a cleaner syntax.

Anyway, they aren't so different if you have to maintain an old system.

#>2.4
class MyClass(object):
    @staticmethod
    def mystaticmethod(params)
        pass
        return

#<2.4
class MyClass(object):

    def mystaticmethod(params)
        '''Static Method'''
        pass
        return
    staticmethod(mystaticmethod)

I'd recommend you add a comment or dostring to the static method screaming that is a static method.

voyager
+1 for the recipe
nikow
+4  A: 

Static methods in python date back to the introduction of the so-called “new-style classes” in Python 2.2. Previous to this, methods on classes were just ordinary functions, stored as attributes on the class:

class OldStyleClass:
    def method(self):
        print "'self' is just the first argument of this function"

instance = OldStyleClass()
OldStyleClass.method(instance) # Just an ordinary function call
print repr(OldStyleClass.method) # "unbound method..."

Method calls on instances were specially handled to automatically bind the instance to the first argument of the function:

instance.method() # 'instance' is automatically passed in as the first parameter
print repr(instance.method) # "bound method..."

In Python 2.2, much of the class system was rethought and reengineered as “new-style classes”—classes that inherit from object. One of the features of new-style classes was “descriptors”, essentially an object in a class that is responsible for describing, getting, and setting the class's attributes. A descriptor has a __get__ method, that gets passed the class and the instance, and should return the requested attribute of the class or instance.

Descriptors made it possible to use a single API to implement complex behaviour for class attributes, like properties, class methods, and static methods. For example, the staticmethod descriptor could be implemented like this:

class staticmethod(object):
    """Create a static method from a function."""

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls=None):
        return self.func

Compare this with a hypothetical pure-python descriptor for an ordinary method, which is used by default for all plain functions in the classes attributes (this is not exactly what happens with method lookup from an instance, but it does handle the automatic 'self' argument):

class method(object):
    """Create a method from a function--it will get the instance
    passed in as its first argument."""

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls=None):
        # Create a wrapper function that passes the instance as first argument
        # to the original function
        def boundmethod(*args, **kwargs):
            return self.func(self, *args, **kwargs)
        return boundmethod

So when you write method = staticmethod(method), you are actually creating a new descriptor whose job it is to return the original function unchanged, and storing this descriptor in the class's "method" attribute.

If that seems like a lot of work to go to just to get the original function back—you’re right, it is. But since normal method calls are the default case, static methods and class methods need to be implemented separately, and descriptors give a way of enabling these and other complex behaviours with one simple API.

As others have already pointed out, the decorator syntax introduced in Python 2.4 gives a more convenient way of declaring static methods, but it is just a syntactic convenience, and doesn't change anything of how static methods work.

See http://www.python.org/doc/2.2.3/whatsnew/sect-rellinks.html and http://users.rcn.com/python/download/Descriptor.htm for more details on the new-style classes and descriptors.

adurdin
+11  A: 

voyager and adurdin do a good job, between them, of explaining what happened: with the introduction of new-style classes and descriptors in Python 2.2, new and deep semantic possibilities arose -- and the most obviously useful examples (static methods, class methods, properties) were supported by built-in descriptor types without any new syntax (the syntax @foo for decorators was added a couple releases later, once the new descriptors had amply proven their real-world usefulness). I'm not really qualified to channel Guido (where's Tim Peters when you need him!-), but I was already a Python committer at the time and participated in these developments, and I can confirm that's indeed what happened.

voyager's observation on this reminding him of C is right on-target: I've long claimed that Python captures more of the "Spirit of C" than any of the languages who have mimicked the syntax of C (braces, parentheses after if/while, etc). "The spirit of C" is actually described in the (non-normative) Rationale part of the ISO C Standard, and comprises five principles (none of which requires braces!-) of which I claim Python matches 4.5 (there are several videos on the web of presentations of mine on "Python for Programmers" where I cover this if you're curious).

In particular, the Spirit of C's "provide only one way to do an operation" matches the Zen of Python's "There should be one-- and preferably only one --obvious way to do it" -- and C and Python, I believe, are the only two widespread languages to explicitly adopt such a design ideal of uniformity and non-redundancy (it's an ideal, and can't be sensibly reached 100% -- e.g. if a and b are integers, a+b and b+a had BETTER be two identically-obvious ways to get their sum!-) -- but it's a goal to aim for!-).

Alex Martelli
I'd still call a + b and b + a "one way to do it", since it's int + int in both cases.
TM
Too bad I can't accept two answers; your answer and Michael's complement each other well.
Uri
@Alex: now you got me hooked. Could you provide a link for "Python for Programmers"? I'm guessing you are referring to this one http://video.google.com/videoplay?docid=1135114630744003385#
voyager
@voyager, that's one; also "painless python for proficient programmers", http://www.youtube.com/watch?v=bDgD9whDfEY for part 1 and I don't know where part 2 is hiding (but I do know it's SOMEwhere on youtube, too...).
Alex Martelli
@Alex: I'll find it, one way or another ;) The first one was quite good. You are responsible of me sleeping an hour and a half less today :)
voyager
voyager
Thanks for the sleuthing @voyager!
Alex Martelli
+2  A: 

The static method situation in Python is a rather direct consequence of the design decisions of first-class everything and everything is an executable statement. As others have stated, staticmethod only became available with new semantics allowed by the Python 2.2 descriptor protocol and made syntactically sweeter by function decorators in Python 2.4. There's a simple reason why static methods have gotten so little attention - they don't extend the power of the language in any way and make syntax only slightly better. Semantically they are the equivalent of plain old functions. That's why they were only implemented when the power of the language grew enough to make them implementable in terms of other language features.

Ants Aasma