How was the syntax chosen for static methods in Python?
Asked Answered
E

8

14

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.

Empale answered 25/9, 2009 at 14:14 Comment(6)
Why the close vote? +1. It's a valid question. I think it has started the other way around, i.e. the instance syntax was deliberately chosen to require explicitly passing self.Yoghurt
it's a discussion/rant and not a question. In python one could use decoratorBunkum
@SilentGhost: To me, it's more like asking the ritual behind a design decision, not a rant.Yoghurt
I'm trying to understand the language design rationale here. There are many questions about these things on SO. I can add a language-design tag if it makes someone happy.Empale
@Uri: you're simply a bit dated, you're supposed to use decorators to do that nowadays, which makes it far more like in other languages.Chrysoberyl
@unwind: I'm writing a library that has to support some legacy code. I've been using the old syntax to avoid potential issues.Empale
M
12

Static methods were added to Python long after classes were (classes were added very early on, possibly even before 1.0; static methods didn't show up until sometime about 2.0). They were implemented as a modification of normal methods — you create a static method object from a function to get a static method, whereas the compiler generates instance methods by default.

As with many things in Python, static methods got introduced and then refined as people used them and wanted better syntax. The initial round was a way of introducing the semantics without adding new syntax to the language (and Python is quite resistant to syntax changes). I'm not Guido, so I'm not exactly sure what was going on in his head and this is somewhat speculative, but Python tends to move slowly, develop incrementally, and refine things as they gain more experience with them (in particular, they don't like adding something until they've figured out the right way to do it. This could have been why there wasn't special syntax for static methods from the beginning).

As mjv indicated, though, there is an easier way now, through some syntax sugar added in 2.2 or 2.3 called "decorators":

@staticmethod
def mystaticmethod(params)
    ...
    return

The @staticmethod syntax is sugar for putting mystaticmethod = staticmethod(mystaticmethod) after the method definition.

Misrule answered 25/9, 2009 at 14:24 Comment(0)
D
11

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!-).

Dementia answered 25/9, 2009 at 15:13 Comment(6)
I'd still call a + b and b + a "one way to do it", since it's int + int in both cases.Marlenemarler
Too bad I can't accept two answers; your answer and Michael's complement each other well.Empale
@Alex: now you got me hooked. Could you provide a link for "Python for Programmers"? I'm guessing you are referring to this one video.google.com/videoplay?docid=1135114630744003385#Cytoplast
@voyager, that's one; also "painless python for proficient programmers", 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...).Dementia
@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 :)Cytoplast
Part 2 of "Painless Python for proficient programmers" youtube.com/watch?v=y7vwZ20SDzc&feature=relatedCytoplast
R
4

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.

Rubirubia answered 25/9, 2009 at 14:54 Comment(0)
G
3

Guido 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 discussion that goes into adding something. I, for one, like that approach. It reminds me of C in that there are no unnecessary 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 docstring to the static method screaming that is a static method.

Gehlenite answered 25/9, 2009 at 14:48 Comment(0)
L
2

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!

Lessen answered 25/9, 2009 at 14:18 Comment(1)
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.Empale
P
2

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.

Phrygian answered 25/9, 2009 at 16:12 Comment(0)
B
1

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.

Bunkum answered 25/9, 2009 at 14:14 Comment(0)
R
0

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.

Reportage answered 25/9, 2009 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.