decorator peculiarity

Scott David Daniels Scott.Daniels at Acm.Org
Fri Dec 17 10:16:21 EST 2004


Diez B. Roggisch wrote:
>     def decorate(func):
>         def _d(*args, **kwargs):
>             do_something()
>             # call func
>             func(*args, **kwargs)
>         return _d
>     @decorate
>     def foo():
>         pass
> [T]he function decorator has to return a function that is bound to the name
> foo in the originating context (module or class) and gets the function
> passed as argument. If I want my decorator ... parametrized...:
>     @decorate(arg)
>     def foo():
>         pass
>     def decorate(arg):
>         def f(func):
>             def _d(*args, **kwargs):
>                 do_something(arg)
>                 # call func
>                 func(*args, **kwargs)
>             return _d
> Now why this layer of indirection?

Let's rewrite your example a bit, it may become obvious.
      def decorate(func):
          def replacement(argspec):
              do_something()
              return func(argspec)
          return replacement
      @decorate
      def foo(argspec): pass
---
      def decorate2():
          def _decorate(func):
              def replacement(argspec):
                  do_something()
                  return func(argspec)
              return replacement
          return _decorate
      @decorate()
      def foo(argspec): pass
---
      def decorate3(arg):
          def _decorate(func):
              def replacement(argspec):
                  do_something()
                  return func(argspec)
              return replacement
          return _decorate
      @decorate3(arg)
      def foo(argspec): pass
---
Clear now? there is no extra indirection.  If you call something on
the @ line, the _result_of_that_call_ should be a decorator function.
If you use my curry recipe in the python cookbook, you can use curry
lower the apparent "indirection":

      def decorate4(arg, func):
          def replacement(argspec):
              do_something()
              return func(argspec)
          return replacement
      @curry(decorate4, arg)
      def foo(argspec): pass

By the way, I _do_ think curry is an appropriate name.  The Curry-Howard
isomorphism shows the equivalence of a functions on tuples with single-
argument functions returning functions.  In a functional language, where
there is no distinction made between a a function with no arg and its
result, the transformation is clear.  With a language like Python, which
pays attention to when a function is invoked, you are better off
returning a function which can take the remaining arguments, and
controlling when the final function is called by returning a function
that can take all the remaining arguments at once.

-Scott David Daniels
Scott.Daniels at Acm.Org



More information about the Python-list mailing list