Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!feeder.erje.net!eu.feeder.erje.net!newsfeed.straub-nv.de!newsreader4.netcologne.de!news.netcologne.de!feeder1.cambriumusenet.nl!82.197.223.103.MISMATCH!feeder3.cambriumusenet.nl!feed.tweaknews.nl!194.109.133.86.MISMATCH!newsfeed.xs4all.nl!newsfeed3.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; '(at': 0.03; '"""': 0.05; 'attribute': 0.05; 'run-time': 0.05; '*args,': 0.07; 'bug.': 0.07; 'calls.': 0.07; 'decorator': 0.07; 'function,': 0.07; 'try:': 0.07; 'urllib2': 0.07; 'python': 0.09; '**kwargs)': 0.09; '**kwargs):': 0.09; 'called.': 0.09; 'naturally': 0.09; 'cc:addr :python-list': 0.10; 'def': 0.10; '>>': 0.16; '(via': 0.16; 'a(object):': 0.16; 'duplicating': 0.16; 'httperror': 0.16; 'httperror,': 0.16; 'indexerror:': 0.16; 'instantiate': 0.16; 'lend': 0.16; 'scope.': 0.16; "time'": 0.16; 'wrappers': 0.16; '\xa0def': 0.16; 'string': 0.17; 'wrote:': 0.17; 'example.': 0.17; 'implementing': 0.17; 'thu,': 0.17; 'jan': 0.18; 'appropriate': 0.20; 'code.': 0.20; 'email addr:gmail.com>': 0.20; 'trying': 0.21; 'import': 0.21; 'do.': 0.21; '31,': 0.22; 'decorators': 0.22; '\xa0if': 0.22; 'defined': 0.22; 'skip:_ 20': 0.22; 'work,': 0.22; "i'd": 0.22; 'cc:2**0': 0.23; '>': 0.23; '(i.e.,': 0.23; 'seems': 0.23; 'raise': 0.24; 'cc:no real name:2**0': 0.24; 'least': 0.25; 'cc:addr:python.org': 0.25; 'header:In-Reply-To:1': 0.25; 'expand': 0.26; 'am,': 0.27; 'right.': 0.27; 'message- id:@mail.gmail.com': 0.27; 'chris': 0.28; 'closer': 0.29; 'declared': 0.29; 'overhead': 0.29; 'wrap': 0.29; 'objects': 0.29; '8bit%:5': 0.29; 'skip:_ 10': 0.29; 'skip:& 10': 0.29; 'class': 0.29; "i'm": 0.29; 'related': 0.30; 'function': 0.30; 'code': 0.31; 'could': 0.32; 'surely': 0.33; 'themselves': 0.33; 'received:google.com': 0.34; 'list': 0.35; 'adds': 0.35; 'jason': 0.35; 'except': 0.36; 'but': 0.36; 'child': 0.36; 'subject:with': 0.36; 'should': 0.36; 'skip:p 20': 0.36; 'possible': 0.37; 'bad': 0.37; 'level': 0.37; 'why': 0.37; 'passed': 0.37; 'rather': 0.37; 'subject:: ': 0.38; 'sure': 0.38; 'instead': 0.39; 'think': 0.40; 'skip:u 10': 0.60; 'more': 0.63; 'behavior': 0.64; 'grab': 0.64; 'here': 0.65; 'risk': 0.66; 'acts': 0.71; '2013': 0.84; "available.'": 0.84; 'basically,': 0.84; 'decorate': 0.84; 'locally': 0.84; '\xa0basically,': 0.84; 'impressions': 0.91; 'to:none': 0.93 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:x-received:in-reply-to:references:date:message-id :subject:from:cc:content-type; bh=LWNYp5pg98nN6FqPGlUCXHFrU4LwEx9k5rt5fVUt7os=; b=aBdpwbWzw/TdyKmXY8Qmo6c5ybf7S8D/3KPS1JPToDq8mAO+auI0HASi5tbObMDEvP 9E3ouA+J6TTjS2mJuas+nd1TKqBpkTFROOa+o8YTKTeYgYiM2q+T8ZixV02Go8WpB4nJ qSgZ5dQKBJM6uexem2bjovvc0jCv7PBoRqq367/BqYCrUnXrib/knohoxy88Du3Hb36u A7UvjCBliYh76gGtru8gl+10qxKfb02rgHBXC52Y5CTYCjeMwPzzNNFpiKhzHu2Y7jri dF6HlOJbTw/+rrrG0CgeTSjHDupwyg5WaRtIcVlTmrUzSnQFvZL23GLd/FzMKn0ykofc 5CMA== MIME-Version: 1.0 X-Received: by 10.50.158.168 with SMTP id wv8mr1863704igb.80.1359654793377; Thu, 31 Jan 2013 09:53:13 -0800 (PST) In-Reply-To: References: <510a053a$0$11104$c3e8da3@news.astraweb.com> Date: Thu, 31 Jan 2013 12:53:13 -0500 Subject: Re: confusion with decorators From: Jason Swails Cc: python-list@python.org Content-Type: multipart/alternative; boundary=14dae9340c49f9f60d04d4994ceb X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 224 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1359654802 news.xs4all.nl 6906 [2001:888:2000:d::a6]:34424 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:38004 --14dae9340c49f9f60d04d4994ceb Content-Type: text/plain; charset=ISO-8859-1 On Thu, Jan 31, 2013 at 11:00 AM, Jason Swails wrote: > > > On Thu, Jan 31, 2013 at 10:28 AM, Chris Angelico 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 --14dae9340c49f9f60d04d4994ceb Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable

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


On Thu, Jan 31, 2013 at 10:28 AM, C= hris Angelico <rosuav@gmail.com> wrote:

>> Well, that surely isn't going to work, because it always decor= ates the
>> same function, the global "fcn".
>
>
> I don't think this is right. =A0fcn is a passed function (at least= if it acts
> as a decorator) that is declared locally in the _protector_decorator s= cope.
> Since newfcn is bound in the same scope and fcn is not defined inside<= br> > 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 sim= ply:
def _protector_decorator(fcn):
=A0 return fcn

? I'm not understanding the purpose here.

Bad example. =A0A better (longer) one that is closer to my tru= e use-case:


from somewhere.exc= eptions import MyTypeError
from somewhere.different import AuthorClass, RemoteAuthorClass
from urllib2 import HTTPError

class A(object):<= /div>
=A0 =A0
=A0 =A0authorclass =3D AuthorClass
=A0 =A0def __init__(self, obj_list):
=A0 =A0 =A0 ""= ;"
=A0 =A0 =A0 Instantiate a list of obj_list objects that m= ay have an "author"
=A0 =A0 =A0 attribute
=A0= =A0 =A0 """
=A0 =A0 =A0 self.things =3D []
=A0 =A0 =A0 for o in obj_list= :
=A0 =A0 =A0 =A0 =A0if not isinstance(o, self.authorclass):
=A0 =A0 =A0 =A0 =A0 =A0 raise MyTypeError('Bad type given to cons= tructor')
=A0 =A0 =A0 =A0 =A0self.things.append(o)

=A0 =A0def _protector(fcn):
=A0 =A0 =A0 def n= ewfcn(self, *args, **kwargs):
=A0 =A0 =A0 =A0 =A0try:
= =A0 =A0 =A0 =A0 =A0 =A0 return fcn(self, *args, **kwargs) # returns a strin= g
=A0 =A0 =A0 =A0 =A0except AttributeError:
=A0 =A0 =A0 =A0 =A0 =A0 return 'Attribute not available.'
=A0 =A0 =A0 =A0 =A0except IndexError:
=A0 =A0 =A0 =A0 =A0 = =A0 return 'Not that many AuthorClasses loaded'

=A0 =A0 =A0 return newfcn

=A0 =A0@_protector
=A0 =A0def author(self, id= x):
=A0 =A0 =A0 return self.things[idx].author

=A0 =A0@_protector
=A0 =A0def description(self, idx):
=A0 =A0 =A0 return self.things[idx].description

=A0 =A0@_protector
=A0 =A0def hobbies(self, i= dx):
=A0 =A0 =A0 return self.things[idx].hobbies

class B(A):
=A0 =A0
=A0 =A0authorclass =3D Rem= oteAuthorClass
=A0 =A0
=A0 =A0def _protector(fcn):
=A0 =A0 =A0 de= f newfcn(self, *args, **kwargs):
=A0 =A0 =A0 =A0 =A0try:
=A0 =A0 =A0 =A0 =A0 =A0 return fcn(self, *args, **kwargs)
=A0 = =A0 =A0 =A0 =A0except AttributeError:
=A0 =A0 =A0 =A0 =A0 =A0 return 'Attribute not available'
=
=A0 =A0 =A0 =A0 =A0except IndexError:
=A0 =A0 =A0 =A0 =A0 = =A0 return 'Not that many RemoteAuthorClasses loaded'
=A0= =A0 =A0 =A0 =A0except HTTPError:
=A0 =A0 =A0 =A0 =A0 =A0 return 'Could not connect'
=A0 = =A0 =A0 return fcn

Basically, while RemoteAuthorCl= ass and AuthorClass are related (via inheritance), the RemoteAuthorClass ha= s the potential for HTTPError's now. =A0I 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. =A0I'm under no im= pressions 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 fun= ctions that need to be called. =A0Basically, wrap at run-time rather than &= #39;compile time' (i.e., when the Python code is 'compiled' int= o class definitions). =A0That way each child of the main class can simply c= hange the wrapping behavior by implementing the wrapping functions instead = of duplicating all of the code. =A0And since this part of the code is not p= erformance-intensive, I don't care about the overhead of extra function= calls.

It seems to me to be the more appropriate course of act= ion here, since decorators don't seem to naturally lend themselves to w= hat I'm trying to do.

--Jason

--14dae9340c49f9f60d04d4994ceb--