Confusion about decorators

Discussion in 'Python' started by Henrik Faber, Dec 12, 2011.

  1. Henrik Faber

    Henrik Faber Guest

    Hi group,

    I'm a bit confused regarding decorators. Recently started playing with
    them with Python3 and wanted (as an excercise) to implement a simple
    type checker first: I know there are lots of them out there, this is
    actually one of the reasons I chose that particular function (to compare
    my solution against other, proven solutions).

    Starting with a blank slate, I did something along the lines of:

    class _TypeCheckedFunction():
    def __init__(self, decoratedfunction):
    self._decoratedfunction = decoratedfunction

    def __call__(self, *args, **kwargs):
    [...] Actual checking

    def typecheck(wrappedfunction):
    checkfunction = _TypeCheckedFunction(wrappedfunction)
    functools.update_wrapper(checkfunction, wrappedfunction)
    return checkfunction

    And decorate my methods like

    @typecheck
    def setbar(self, bar: str):

    This works somewhat. The problem is, however, when the method is
    actually called. This is what happens:

    1. The decorator is called upon import of the decorated class. It
    creates a _TypeCheckedFunction(setbar) object.
    2. When setbar is actually called (blubb.setbar("fooobar")), the
    __call__ method of the previously created _TypeCheckedFunction is invoked.
    3. When trying to call self._decoratedfunction from within that object,
    this fails: "self" is missing! self._decoratedfunction is only the
    *function*, not the bound function of the object that contains setbar().
    Therefore I cannot proceed here.

    Solutions that I have seen working usually consist of two functions
    wrapped in each other, but I do not know why the additional introduction
    of a class makes everything fail.

    Can someone please enlighten me?

    Best regards,
    Henrik
     
    Henrik Faber, Dec 12, 2011
    #1
    1. Advertising

  2. On 12/12/2011 01:27 PM, Henrik Faber wrote:
    > Hi group,
    >
    > I'm a bit confused regarding decorators. Recently started playing with
    > them with Python3 and wanted (as an excercise) to implement a simple
    > type checker first: I know there are lots of them out there, this is
    > actually one of the reasons I chose that particular function (to compare
    > my solution against other, proven solutions).
    >
    > Starting with a blank slate, I did something along the lines of:
    >
    > class _TypeCheckedFunction():
    > def __init__(self, decoratedfunction):
    > self._decoratedfunction = decoratedfunction
    >
    > def __call__(self, *args, **kwargs):
    > [...] Actual checking
    >
    > def typecheck(wrappedfunction):
    > checkfunction = _TypeCheckedFunction(wrappedfunction)
    > functools.update_wrapper(checkfunction, wrappedfunction)
    > return checkfunction
    >
    > And decorate my methods like
    >
    > @typecheck
    > def setbar(self, bar: str):
    >
    > This works somewhat. The problem is, however, when the method is
    > actually called. This is what happens:
    >
    > 1. The decorator is called upon import of the decorated class. It
    > creates a _TypeCheckedFunction(setbar) object.
    > 2. When setbar is actually called (blubb.setbar("fooobar")), the
    > __call__ method of the previously created _TypeCheckedFunction is invoked.
    > 3. When trying to call self._decoratedfunction from within that object,
    > this fails: "self" is missing! self._decoratedfunction is only the
    > *function*, not the bound function of the object that contains setbar().
    > Therefore I cannot proceed here.
    >
    > Solutions that I have seen working usually consist of two functions
    > wrapped in each other, but I do not know why the additional introduction
    > of a class makes everything fail.
    >
    > Can someone please enlighten me?
    >
    > Best regards,
    > Henrik


    Not sure how that could work in general, what does "bar: str" should do?
    Is that a dictionary?

    Anyway there is already an implementation if you're interested for type
    checking:
    http://oakwinter.com/code/typecheck/

    You can have a look at how they do it.
     
    Andrea Crotti, Dec 12, 2011
    #2
    1. Advertising

  3. Henrik Faber

    Henrik Faber Guest

    On 12.12.2011 14:37, Andrea Crotti wrote:
    > On 12/12/2011 01:27 PM, Henrik Faber wrote:
    >> Hi group,
    >>
    >> I'm a bit confused regarding decorators. Recently started playing with
    >> them with Python3 and wanted (as an excercise) to implement a simple
    >> type checker first: I know there are lots of them out there, this is
    >> actually one of the reasons I chose that particular function (to compare
    >> my solution against other, proven solutions).

    >
    > Not sure how that could work in general, what does "bar: str" should do?
    > Is that a dictionary?


    No. It's PEP 3107 function annotations.

    > Anyway there is already an implementation if you're interested for type
    > checking:
    > http://oakwinter.com/code/typecheck/


    *sigh* no, not really -- this is exactly why I wrote "I know there are
    lots of them out there". I've actually seen and run
    http://code.activestate.com/recipes/577299-method-signature-type-checking-decorator-for-pytho/

    However, this doesn't do it for me -- I want to know why my solution
    fails, not just use some other solution without really understanding it.
    I really would like to understand what's going on.

    I'm especially puzzled about the fact that in my solution, __call__ is
    called with only the method's arguments (i.e. "fooobar") in my example
    instead of two arguments (self, "fooobar").

    Best regards,
    Henrik
     
    Henrik Faber, Dec 12, 2011
    #3
  4. On 12 December 2011 13:27, Henrik Faber <> wrote:
    > Hi group,
    >
    > I'm a bit confused regarding decorators. Recently started playing with
    > them with Python3 and wanted (as an excercise) to implement a simple
    > type checker first: I know there are lots of them out there, this is
    > actually one of the reasons I chose that particular function (to compare
    > my solution against other, proven solutions).
    >
    > Starting with a blank slate, I did something along the lines of:
    >
    > class _TypeCheckedFunction():
    >        def __init__(self, decoratedfunction):
    >                self._decoratedfunction = decoratedfunction
    >
    >        def __call__(self, *args, **kwargs):
    >                [...] Actual checking
    >
    > def typecheck(wrappedfunction):
    >        checkfunction = _TypeCheckedFunction(wrappedfunction)
    >        functools.update_wrapper(checkfunction, wrappedfunction)
    >        return checkfunction
    >
    > And decorate my methods like
    >
    >        @typecheck
    >        def setbar(self, bar: str):
    >
    > This works somewhat. The problem is, however, when the method is
    > actually called. This is what happens:
    >
    > 1. The decorator is called upon import of the decorated class. It
    > creates a _TypeCheckedFunction(setbar) object.
    > 2. When setbar is actually called (blubb.setbar("fooobar")), the
    > __call__ method of the previously created _TypeCheckedFunction is invoked..
    > 3. When trying to call self._decoratedfunction from within that object,
    > this fails: "self" is missing! self._decoratedfunction is only the
    > *function*, not the bound function of the object that contains setbar().
    > Therefore I cannot proceed here.
    >
    > Solutions that I have seen working usually consist of two functions
    > wrapped in each other, but I do not know why the additional introduction
    > of a class makes everything fail.
    >
    > Can someone please enlighten me?


    You can (need to?) use the descriptor protocol to deal with methods.

    from functools import partial

    class _TypeCheckedFunction():
    def __init__(self, decoratedfunction):
    self._decoratedfunction = decoratedfunction

    def __call__(self, *args, **kwargs):
    [...] Actual checking

    def __get__(self, obj, objtype):
    return partial(self, obj)

    (Untested)

    HTH

    --
    Arnaud
     
    Arnaud Delobelle, Dec 12, 2011
    #4
  5. Henrik Faber

    Henrik Faber Guest

    On 12.12.2011 14:45, Arnaud Delobelle wrote:

    >> Can someone please enlighten me?

    >
    > You can (need to?) use the descriptor protocol to deal with methods.
    >
    > from functools import partial

    [...]
    > def __get__(self, obj, objtype):
    > return partial(self, obj)


    Whoa. This is absolutely fantastic, it now works as expected (I get a
    reference to "self").

    I am very amazed -- I've been programming Python for about 5 years now
    and have never even come close to something as a "descriptor protocol".
    Python never ceases to amaze me. Do you have any beginners guide how
    this works? The Pydoc ("Data Model") is comprehensive, but I really
    don't know where to start to look.

    Still amazed!

    Best regards,
    Henrik
     
    Henrik Faber, Dec 12, 2011
    #5
  6. On 12 December 2011 13:52, Henrik Faber <> wrote:
    > On 12.12.2011 14:45, Arnaud Delobelle wrote:
    >
    >>> Can someone please enlighten me?

    >>
    >> You can (need to?) use the descriptor protocol to deal with methods.
    >>
    >> from functools import partial

    > [...]
    >>        def __get__(self, obj, objtype):
    >>                return partial(self, obj)

    >
    > Whoa. This is absolutely fantastic, it now works as expected (I get a
    > reference to "self").
    >
    > I am very amazed -- I've been programming Python for about 5 years now
    > and have never even come close to something as a "descriptor protocol".
    > Python never ceases to amaze me. Do you have any beginners guide how
    > this works? The Pydoc ("Data Model") is comprehensive, but I really
    > don't know where to start to look.


    Well, I've been using Python for 10 years :) The best reference I know is:

    http://users.rcn.com/python/download/Descriptor.htm

    --
    Arnaud
     
    Arnaud Delobelle, Dec 12, 2011
    #6
  7. Henrik Faber

    Henrik Faber Guest

    On 12.12.2011 15:01, Arnaud Delobelle wrote:

    >> I am very amazed -- I've been programming Python for about 5 years now
    >> and have never even come close to something as a "descriptor protocol".
    >> Python never ceases to amaze me. Do you have any beginners guide how
    >> this works? The Pydoc ("Data Model") is comprehensive, but I really
    >> don't know where to start to look.

    >
    > Well, I've been using Python for 10 years :) The best reference I know is:
    >
    > http://users.rcn.com/python/download/Descriptor.htm


    Everyone starts out as a Padawan and I am no exception :)

    Maybe five years from now I'll also have made my way to be a Python Jedi
    and also shake the ins and outs of descriptors out of my sleeve :)

    But I can only repeat myself: Python is such an exceptional language,
    the more and more I know about it, the more I fall in love! Fantastic. I
    wish we had these types of language when I was a kid!

    Best regards and thanks again,
    Henrik
     
    Henrik Faber, Dec 12, 2011
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Strorm
    Replies:
    12
    Views:
    1,828
    Michael Strorm
    Apr 20, 2005
  2. Michael Sparks

    Using metaclasses to play with decorators.

    Michael Sparks, Jun 15, 2004, in forum: Python
    Replies:
    4
    Views:
    346
    Michele Simionato
    Jun 18, 2004
  3. Arien Malec

    PEP 318 decorators are not Decorators

    Arien Malec, Aug 13, 2004, in forum: Python
    Replies:
    11
    Views:
    579
    Arien Malec
    Aug 16, 2004
  4. Jason Swails

    confusion with decorators

    Jason Swails, Jan 31, 2013, in forum: Python
    Replies:
    10
    Views:
    223
    88888 Dihedral
    Feb 1, 2013
  5. Dave Angel

    Re: confusion with decorators

    Dave Angel, Jan 31, 2013, in forum: Python
    Replies:
    0
    Views:
    151
    Dave Angel
    Jan 31, 2013
Loading...

Share This Page