Make staticmethod objects callable?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Nicolas Fleury

    Make staticmethod objects callable?

    Hi everyone,
    I was wondering if it would make sense to make staticmethod objects
    callable, so that the following code would work:

    class A:
    @staticmethod
    def foo(): pass
    bar = foo()

    I understand staticmethod objects don't need to implement __call__ for
    their other use cases, but would it still make sense to implement
    __call__ for that specific use case? Would it be error-prone in any way?

    Thx and regards,
    Nicolas
  • Steven Bethard

    #2
    Re: Make staticmethod objects callable?

    Nicolas Fleury wrote:[color=blue]
    > I was wondering if it would make sense to make staticmethod objects
    > callable, so that the following code would work:
    >
    > class A:
    > @staticmethod
    > def foo(): pass
    > bar = foo()[/color]

    Do you have a real-world use case? I pretty much never use
    staticmethods (preferring instead to use module-level functions) so I'm
    having a hard time coming up with some code where this would be really
    useful. I'm also a little wary of breaking the parallel between
    classmethod and staticmethod which are currently *just* descriptors
    (without any other special functionality).

    Maybe instead of making just staticmethods callable, both staticmethods
    and classmethods could gain an 'im_func' attribute like bound and
    unbound methods have?

    STeVe

    Comment

    • Felipe Almeida Lessa

      #3
      Re: Make staticmethod objects callable?

      Em Ter, 2006-02-28 às 15:17 -0500, Nicolas Fleury escreveu:[color=blue]
      > class A:
      > @staticmethod
      > def foo(): pass
      > bar = foo()[/color]

      # Why not:

      def foo(): pass

      class A:
      bar = foo()
      foo = staticmethod(fo o)

      --
      "Quem excele em empregar a força militar subjulga os exércitos dos
      outros povos sem travar batalha, toma cidades fortificadas dos outros
      povos sem as atacar e destrói os estados dos outros povos sem lutas
      prolongadas. Deve lutar sob o Céu com o propósito primordial da
      'preservação' . Desse modo suas armas não se embotarão, e os ganhos
      poderão ser preservados. Essa é a estratégia para planejar ofensivas."

      -- Sun Tzu, em "A arte da guerra"

      Comment

      • Nicolas Fleury

        #4
        Re: Make staticmethod objects callable?

        Felipe Almeida Lessa wrote:[color=blue]
        > Em Ter, 2006-02-28 às 15:17 -0500, Nicolas Fleury escreveu:
        >[color=green]
        >>class A:
        >> @staticmethod
        >> def foo(): pass
        >> bar = foo()[/color]
        >
        >
        > # Why not:
        >
        > def foo(): pass
        >
        > class A:
        > bar = foo()
        > foo = staticmethod(fo o)
        >[/color]

        Well, you could even do:

        class A:
        def foo(): pass
        bar = foo()
        staticmethod(fo o)

        But it's a bit sad to have a special syntax for decorators then. It's a
        usability problem, nothing to do with functionality. There's obvious
        workarounds, but IMHO any user saying "it should just work" is at least
        partly right.

        Regards,
        Nicolas

        Comment

        • Nicolas Fleury

          #5
          Re: Make staticmethod objects callable?

          Steven Bethard wrote:[color=blue]
          > Nicolas Fleury wrote:
          >[color=green]
          >> I was wondering if it would make sense to make staticmethod objects
          >> callable, so that the following code would work:
          >>
          >> class A:
          >> @staticmethod
          >> def foo(): pass
          >> bar = foo()[/color]
          >
          > Do you have a real-world use case? I pretty much never use
          > staticmethods (preferring instead to use module-level functions) so I'm
          > having a hard time coming up with some code where this would be really
          > useful. I'm also a little wary of breaking the parallel between
          > classmethod and staticmethod which are currently *just* descriptors
          > (without any other special functionality).[/color]

          Well, IMHO, if staticmethod is worth being standard, than calling these
          static methods inside the class definition is worth being supported. To
          be honest I don't use static methods like you, but users come see me
          asking "Why?" and I find their code logically structured so I conclude
          there's a usability problem here.
          [color=blue]
          > Maybe instead of making just staticmethods callable, both staticmethods
          > and classmethods could gain an 'im_func' attribute like bound and
          > unbound methods have?[/color]

          I don't like it either (__get__ can be called anyway). My problem is
          the expectation of the user. I wonder if it should just work. Adding a
          few lines of code in staticmethod is worth it if it avoids all these
          questions over and over again, no?

          Regards,
          Nicolas

          Comment

          • Murali

            #6
            Re: Make staticmethod objects callable?

            You have a text-database, each record has some "header info" and some
            data (text-blob). e.g.

            --------------------
            <HEADER>
            name = "Tom"
            phone = "312-996-XXXX"
            </HEADER>
            I last met tom in 1998. He was still single then.
            blah
            blah
            <HEADER>
            name = "John"
            birthday = "1976-Mar-12"
            </HEADER>
            I need to ask him his email when I see him next.
            ------------------

            I use this format for a file to keep track of tit bits of information.
            Lets say the file has several hundred records. I know want to say
            generate a birthday list of people and their birthdays. Ofcourse along
            with that I also need the "text-blob" (because I dont want to send a
            birthday card to a person I dont like). In order to do this I execute a
            script

            ../filter --input=database. txt --condn='similar( name,"tom")'.

            The way it is implemented is simple. Have a class which has dict as its
            base class. For each record the text between <hEADER> and </HEADER> is
            executed with the class instance as "locals()". Now that I have a list
            of class instances, I just exec the condition and those instances where
            it evaluates True comprise the output text file.

            To make the user, not have to know too much python, one would like to
            define "functions" which can be used. For eg. similar would have the
            following definition

            @staticmethod
            def similar(regexp, str):
            return re.match("(?i)^ .*%s.*$" % regexp, str) != None

            This way the "locals()" dictionary in the exec'ed environment has
            access to the function similar (if similar was callable). At the same
            time, I can enclose all these functions in their own name space (as
            static methods of a class).

            Right now, I declare all these "helper" functions in a different
            module, and "attach" the "helper" functions as keys into the
            dictionary. If only staticmethods were callable. For some reason (I
            dont recall why) the idea of converting the staticmethod into a
            callable still did not work, e.g.

            class Callable:
            def __init__(self,m ethod):
            self.__call__ = method

            class Record(dict):

            @staticmethod
            def similar(regexp, string):
            ....

            self['similar'] = Callable(simila r)

            The above did not work. The error message was still related to a
            staticmethod not being a callable.

            - Murali

            Comment

            • Steven D'Aprano

              #7
              Re: Make staticmethod objects callable?

              Nicolas Fleury wrote:
              [color=blue]
              > Hi everyone,
              > I was wondering if it would make sense to make staticmethod objects
              > callable, so that the following code would work:[/color]

              This is one of the more unintuitive areas of Python,
              with side effects and different behaviour depending on
              whether code is called inside or outside a class:
              [color=blue][color=green][color=darkred]
              >>> class Parrot:[/color][/color][/color]
              .... def speak():
              .... return "dead parrot"
              .... speak = staticmethod(sp eak)
              .... def playdead():
              .... return "still dead"
              ....[color=blue][color=green][color=darkred]
              >>> type(Parrot.spe ak)[/color][/color][/color]
              <type 'function'>[color=blue][color=green][color=darkred]
              >>> type(Parrot.pla ydead)[/color][/color][/color]
              <type 'instancemethod '>

              So, based on this evidence, staticmethod() converts an
              instance method into an ordinary function. Parrot.speak
              certainly behaves like an ordinary function.
              [color=blue][color=green][color=darkred]
              >>> callable(Parrot .speak)[/color][/color][/color]
              True[color=blue][color=green][color=darkred]
              >>> Parrot.speak()[/color][/color][/color]
              'dead parrot'


              But now try to do the same outside of a class:
              [color=blue][color=green][color=darkred]
              >>> f = staticmethod(Pa rrot.playdead)
              >>> type(f)[/color][/color][/color]
              <type 'staticmethod'>

              f is not a function?
              [color=blue][color=green][color=darkred]
              >>> Parrot.playdead = staticmethod(Pa rrot.playdead)
              >>> type(Parrot.pla ydead)[/color][/color][/color]
              <type 'instancemethod '>

              So, based on this evidence, staticmethod() inside a
              class definition converts instance methods to
              functions. Outside a class definition, staticmethod()
              does one of two things: it either converts an instance
              method to a static method, or if the output is assigned
              to a class attribute, it leaves it as an instance method.

              Hmmm.


              [color=blue]
              > class A:
              > @staticmethod
              > def foo(): pass
              > bar = foo()[/color]

              Here is a work around:
              [color=blue][color=green][color=darkred]
              >>> class A:[/color][/color][/color]
              .... def foo(): return "foo foo foo"
              .... foo = staticmethod(fo o)
              .... def __init__(self):
              .... if not hasattr(self.__ class__, "bar"):
              .... self.__class__. bar = self.foo()
              ....[color=blue][color=green][color=darkred]
              >>> dir(A)[/color][/color][/color]
              ['__doc__', '__init__', '__module__', 'foo'][color=blue][color=green][color=darkred]
              >>> a = A()
              >>> dir(A)[/color][/color][/color]
              ['__doc__', '__init__', '__module__', 'bar', 'foo'][color=blue][color=green][color=darkred]
              >>> a.foo()[/color][/color][/color]
              'foo foo foo'[color=blue][color=green][color=darkred]
              >>> a.bar[/color][/color][/color]
              'foo foo foo'


              Here is a more interesting example:
              [color=blue][color=green][color=darkred]
              >>> class B:[/color][/color][/color]
              .... def foo():
              .... return lambda x: x+1
              .... foo = staticmethod(fo o)
              .... def __init__(self):
              .... if not hasattr(self.__ class__, "bar"):
              .... self.__class__. bar = \
              .... staticmethod(se lf.foo())
              ....[color=blue][color=green][color=darkred]
              >>> dir(B)[/color][/color][/color]
              ['__doc__', '__init__', '__module__', 'foo'][color=blue][color=green][color=darkred]
              >>> b = B()
              >>> dir(B)[/color][/color][/color]
              ['__doc__', '__init__', '__module__', 'bar', 'foo'][color=blue][color=green][color=darkred]
              >>> b.foo()[/color][/color][/color]
              <function <lambda> at 0xb7f70c6c>[color=blue][color=green][color=darkred]
              >>> b.bar[/color][/color][/color]
              <function <lambda> at 0xb7f70c34>[color=blue][color=green][color=darkred]
              >>> b.bar(3)[/color][/color][/color]
              4


              Hope this helps.



              --
              Steven.

              Comment

              • Steven Bethard

                #8
                Re: Make staticmethod objects callable?

                Steven D'Aprano wrote:[color=blue]
                > So, based on this evidence, staticmethod() inside a class definition
                > converts instance methods to functions. Outside a class definition,
                > staticmethod() does one of two things: it either converts an instance
                > method to a static method, or if the output is assigned to a class
                > attribute, it leaves it as an instance method.[/color]

                This is exactly why I'm concerned about augmenting staticmethod's
                behavior. When people run into this, it should be the opportunity to
                explain to them how descriptors work. Descriptors are hugely important
                in the new object system, and even if we hide them by giving
                staticmethod a __call__, we'll still run into problems when people try
                to do:

                class C(object):
                @classmethod
                def foo(cls):
                print cls
                bar = foo(None)

                Then, not only do we have to explain how descriptors work, but we also
                have to explain why staticmethod has a __call__, and classmethod doesn't.

                (For anyone else out there reading who doesn't already know this, Steven
                D'Aprano's comments are easily explained by noting that the __get__
                method of staticmethod objects returns functions, and classes always
                call the __get__ methods of descriptors when those descriptors are class
                attributes:
                [color=blue][color=green][color=darkred]
                >>> class C(object):[/color][/color][/color]
                .... @staticmethod
                .... def foo():
                .... pass
                .... print foo
                ....
                <staticmethod object at 0x00E73950>[color=blue][color=green][color=darkred]
                >>> print C.foo[/color][/color][/color]
                <function foo at 0x00E80330>[color=blue][color=green][color=darkred]
                >>> @staticmethod[/color][/color][/color]
                .... def foo():
                .... pass
                ....[color=blue][color=green][color=darkred]
                >>> print foo[/color][/color][/color]
                <staticmethod object at 0x00E73990>[color=blue][color=green][color=darkred]
                >>> print foo.__get__(C, None)[/color][/color][/color]
                <function foo at 0x00E80130>

                Yes, you have to explain descriptors, but at the point that you start
                trying to do funny things with staticmethods and classmethods, I think
                you need to start learning about them anyway.)

                All that said, you should probably just submit a patch and see what
                happens. I'll make a brief comment on it along the above lines, but
                since I'm not a committer, it's not really worth your time to try to
                convince me. ;)

                STeVe

                Comment

                • Nicolas Fleury

                  #9
                  Re: Make staticmethod objects callable?

                  Steven Bethard wrote:[color=blue]
                  > ...
                  > Yes, you have to explain descriptors, but at the point that you start
                  > trying to do funny things with staticmethods and classmethods, I think
                  > you need to start learning about them anyway.)[/color]

                  That's all good points, but IMHO, descriptors are a much more advanced
                  Python feature than static methods, especially for programmers from
                  other backgrounds, like Java/C#/C++. We basically have the choice
                  between hiding something unnecessarily complex or force to understand a
                  useful feature;)
                  [color=blue]
                  > All that said, you should probably just submit a patch and see what
                  > happens. I'll make a brief comment on it along the above lines, but
                  > since I'm not a committer, it's not really worth your time to try to
                  > convince me. ;)[/color]

                  I might do it, but even if I'm not a commiter, I'll continue trying to
                  convince myself;)

                  Nicolas

                  Comment

                  • Terry Reedy

                    #10
                    Re: Make staticmethod objects callable?


                    "Steven D'Aprano" <steve@REMOVEME cyber.com.au> wrote in message
                    news:44055BE0.8 030409@REMOVEME cyber.com.au...[color=blue][color=green][color=darkred]
                    > >>> class Parrot:[/color][/color]
                    > ... def speak():
                    > ... return "dead parrot"
                    > ... speak = staticmethod(sp eak)
                    > ... def playdead():
                    > ... return "still dead"
                    > ...[color=green][color=darkred]
                    > >>> type(Parrot.spe ak)[/color][/color]
                    > <type 'function'>[color=green][color=darkred]
                    > >>> type(Parrot.pla ydead)[/color][/color]
                    > <type 'instancemethod '>
                    >
                    > So, based on this evidence, staticmethod() converts an
                    > instance method into an ordinary function. Parrot.speak
                    > certainly behaves like an ordinary function.[/color]

                    Actually, staticmethod() prevents an ordinary function from being converted
                    to (wrapped as) a method upon access via the class.
                    [color=blue][color=green][color=darkred]
                    >>> class C(object):[/color][/color][/color]
                    def f(s): pass
                    [color=blue][color=green][color=darkred]
                    >>> type(C.__dict__['f'])[/color][/color][/color]
                    <type 'function'>[color=blue][color=green][color=darkred]
                    >>> type(C.f)[/color][/color][/color]
                    <type 'instancemethod '>

                    As to the general topic: my impression is that staticmethod was a rather
                    optional addon with limited usage and that beginners hardly need to know
                    about it.

                    Terry Jan Reedy



                    Comment

                    • Steven D'Aprano

                      #11
                      Re: Make staticmethod objects callable?

                      On Wed, 01 Mar 2006 08:32:20 -0700, Steven Bethard wrote:
                      [color=blue]
                      > (For anyone else out there reading who doesn't already know this, Steven
                      > D'Aprano's comments are easily explained by noting that the __get__
                      > method of staticmethod objects returns functions, and classes always
                      > call the __get__ methods of descriptors when those descriptors are class
                      > attributes:[/color]

                      ....

                      A usage of the word "easily" that I am unfamiliar with.

                      *wink*

                      Why all the indirection to implement something which is, conceptually,
                      the same as an ordinary function?



                      --
                      Steven.

                      Comment

                      Working...