Path: csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!selfless.tophat.at!newsfeed.xs4all.nl!newsfeed5.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; 'else:': 0.03; 'args': 0.05; 'subject:Python': 0.06; 'char': 0.07; '__name__': 0.09; 'decorator': 0.09; 'from:addr:ethan': 0.09; 'from:addr:stoneleaf.us': 0.09; 'from:name:ethan furman': 0.09; 'func': 0.09; 'message-id:@stoneleaf.us': 0.09; 'none:': 0.09; 'quickly.': 0.09; 'received:gator410.hostgator.com': 0.09; '~ethan~': 0.09; 'syntax': 0.11; 'wed,': 0.12; 'wrote:': 0.15; "'\\n')": 0.16; "'__main__':": 0.16; '**kwargs)': 0.16; '**kwargs):': 0.16; '*args,': 0.16; '50)': 0.16; 'callable,': 0.16; 'received:72.11': 0.16; 'received:72.11.125': 0.16; 'received:72.11.125.166': 0.16; 'argument': 0.16; 'cc:addr:python- list': 0.16; 'pm,': 0.16; 'def': 0.16; 'written': 0.17; 'example.': 0.19; 'cc:2**0': 0.21; 'cc:no real name:2**0': 0.22; 'header:In-Reply-To:1': 0.22; 'optional': 0.23; 'way?': 0.23; 'code': 0.24; 'keyword': 0.25; 'function': 0.26; 'depends': 0.28; 'bit': 0.28; 'cc:addr:python.org': 0.30; 'kelly': 0.30; 'class': 0.31; 'least': 0.31; "won't": 0.32; 'header:User-Agent:1': 0.34; 'none': 0.35; 'skip:@ 10': 0.35; 'pretty': 0.35; 'passed': 0.37; 'takes': 0.38; 'subject:: ': 0.38; 'two': 0.38; 'case': 0.39; 'either': 0.39; 'subject:with': 0.39; 'course.': 0.63; 'received:websitewelcome.com': 0.65; 'adapt': 0.67; 'discipline': 0.67; 'received:184': 0.67; 'bright': 0.84; 'directly?': 0.84 Date: Wed, 29 Jun 2011 14:29:45 -0700 From: Ethan Furman User-Agent: Thunderbird 1.5.0.10 (Windows/20070221) MIME-Version: 1.0 To: Ian Kelly Subject: Re: Using decorators with argument in Python References: <4e0a0a8b$1@dnews.tpgi.com.au> <4E0B5F7C.7030802@codicesoftware.com> <4E0B7D6A.1080404@stoneleaf.us> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - gator410.hostgator.com X-AntiAbuse: Original Domain - python.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - stoneleaf.us X-BWhitelist: no X-Source: X-Source-Args: X-Source-Dir: X-Source-Sender: mail.admailinc.com ([192.168.10.136]) [72.11.125.166]:1896 X-Source-Auth: ethan+stoneleaf.us X-Email-Count: 1 X-Source-Cap: dG9idWs7dG9idWs7Z2F0b3I0MTAuaG9zdGdhdG9yLmNvbQ== Cc: python-list@python.org X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.12 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: 102 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1309382125 news.xs4all.nl 21798 [2001:888:2000:d::a6]:45849 X-Complaints-To: abuse@xs4all.nl Xref: x330-a1.tempe.blueboxinc.net comp.lang.python:8585 Ian Kelly wrote: > On Wed, Jun 29, 2011 at 1:30 PM, Ethan Furman wrote: >> How about just having one bit of code that works either way? > > How would you adapt that code if you wanted to be able to decorate a > function that takes arguments? 8<---------------------------------------------------------------- class enclose(object): func = None def __init__(self, char='#'): self.char = char if callable(char): # was a function passed in directly? self.char = '#' # use default char self.func = char def __call__(self, func=None, *args, **kwargs): if self.func is None: self.func = func return self if func is not None: args = (func, ) + args return self._call(*args, **kwargs) def _call(self, *args, **kwargs): print("\n" + self.char * 50) self.func(*args, **kwargs) print(self.char * 50 + '\n') if __name__ == '__main__': @enclose def test1(): print('Spam!') @enclose('-') def test2(): print('Eggs!') @enclose def test3(string): print(string) @enclose('^') def test4(string): print(string) test1() test2() test3('Python') test4('Rules! ;)') 8<---------------------------------------------------------------- > This also won't work if the argument to the decorator is itself a > callable, such as in the OP's example. Indeed. In that case you need two keywords to __init__, and the discipline to always use the keyword syntax at least for the optional function paramater. On the bright side, if one forgets, it blows up pretty quickly. Whether it's worth the extra effort depends on the programmer's tastes, of course. 8<---------------------------------------------------------------- class enclose(object): func = None pre_func = None def __init__(self, dec_func=None, opt_func=None): if opt_func is None: if dec_func is not None: # was written without ()'s self.func = dec_func else: self.pre_func = opt_func def __call__(self, func=None, *args, **kwargs): if self.func is None: self.func = func return self if func is not None: args = (func, ) + args if self.pre_func is not None: self.pre_func() return self._call(*args, **kwargs) def _call(self, *args, **kwargs): print("\n" + '~' * 50) self.func(*args, **kwargs) print('~' * 50 + '\n') if __name__ == '__main__': def some_func(): print('some func here') @enclose def test1(): print('Spam!') @enclose(opt_func=some_func) def test2(): print('Eggs!') @enclose def test3(string): print(string) @enclose(opt_func=some_func) def test4(string): print(string) test1() test2() test3('Python') test4('Rules! ;)') 8<---------------------------------------------------------------- ~Ethan~