confusion with decorators

Jason Swails jason.swails at gmail.com
Thu Jan 31 12:53:13 EST 2013


On Thu, Jan 31, 2013 at 11:00 AM, Jason Swails <jason.swails at gmail.com>wrote:

>
>
> On Thu, Jan 31, 2013 at 10:28 AM, Chris Angelico <rosuav at gmail.com> wrote:
>
>>
>> >> Well, that surely isn't going to work, because it always decorates the
>> >> same function, the global "fcn".
>> >
>> >
>> > I don't think this is right.  fcn is a passed function (at least if it
>> acts
>> > as a decorator) that is declared locally in the _protector_decorator
>> scope.
>> > Since newfcn is bound in the same scope and fcn is not defined inside
>> > newfcn, I'm pretty sure that newfcn will just grab the fcn passed into
>> the
>> > decorator.
>>
>> Yet it adds a level of indirection that achieves nothing. Why not simply:
>> def _protector_decorator(fcn):
>>   return fcn
>>
>> ? I'm not understanding the purpose here.
>>
>
> Bad example.  A better (longer) one that is closer to my true use-case:
>
>
> from somewhere.exceptions import MyTypeError
> from somewhere.different import AuthorClass, RemoteAuthorClass
> from urllib2 import HTTPError
>
> class A(object):
>
>    authorclass = AuthorClass
>
>    def __init__(self, obj_list):
>       """
>       Instantiate a list of obj_list objects that may have an "author"
>       attribute
>       """
>       self.things = []
>       for o in obj_list:
>          if not isinstance(o, self.authorclass):
>             raise MyTypeError('Bad type given to constructor')
>          self.things.append(o)
>
>    def _protector(fcn):
>       def newfcn(self, *args, **kwargs):
>          try:
>             return fcn(self, *args, **kwargs) # returns a string
>          except AttributeError:
>             return 'Attribute not available.'
>          except IndexError:
>             return 'Not that many AuthorClasses loaded'
>
>       return newfcn
>
>    @_protector
>    def author(self, idx):
>       return self.things[idx].author
>
>    @_protector
>    def description(self, idx):
>       return self.things[idx].description
>
>    @_protector
>    def hobbies(self, idx):
>       return self.things[idx].hobbies
>
> class B(A):
>
>    authorclass = RemoteAuthorClass
>
>    def _protector(fcn):
>       def newfcn(self, *args, **kwargs):
>          try:
>             return fcn(self, *args, **kwargs)
>          except AttributeError:
>             return 'Attribute not available'
>          except IndexError:
>             return 'Not that many RemoteAuthorClasses loaded'
>          except HTTPError:
>             return 'Could not connect'
>       return fcn
>
> Basically, while RemoteAuthorClass and AuthorClass are related (via
> inheritance), the RemoteAuthorClass has the potential for HTTPError's now.
>  I could just expand the A class decorator to catch the HTTPError, but
> since that should not be possible in AuthorClass, I'd rather not risk
> masking a bug.  I'm under no impressions that the above code will decorate
> A-inherited functions with the B-decorator (I know it won't), but that's
> the effect I'm trying to achieve...
>

The approach I'm switching to here is to make the decorators wrappers
instead that are passed the functions that need to be called.  Basically,
wrap at run-time rather than 'compile time' (i.e., when the Python code is
'compiled' into class definitions).  That way each child of the main class
can simply change the wrapping behavior by implementing the wrapping
functions instead of duplicating all of the code.  And since this part of
the code is not performance-intensive, I don't care about the overhead of
extra function calls.

It seems to me to be the more appropriate course of action here, since
decorators don't seem to naturally lend themselves to what I'm trying to do.

--Jason
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20130131/8902b193/attachment.html>


More information about the Python-list mailing list