generator expressions: performance anomaly?

Steven Bethard steven.bethard at gmail.com
Wed Jan 19 01:09:57 EST 2005


Bengt Richter wrote:
> On Tue, 18 Jan 2005 17:38:20 -0700, Steven Bethard <steven.bethard at gmail.com> wrote:
> 
> 
>>Bengt Richter wrote:
>>
>>>Which make me wonder what plans there are for providing a better
>>>mechanism than default arguments as a way of initializing local function
>>>variables. Nested def's to create a closure with initialized values is
>>>pretty crufty for that, IMO.
>>
>>What about using a class?  Associating functions with data is pretty 
>>much what they're for...
>>
>>
>>>Maybe extending the default argument space
>>>with whatever comes after e.g. a triple star delimiter in the argument list,
>>>but which wouldn't be counted as part of the normal arguments? E.g.,
>>>
>>>    def foo(x, y=123, *args, **kw, *** i=1, deftime=time.ctime()):
>>>        return x*y, kw.get('which_time')=='now' and time.ctime() or deftime
>>
>>If what you want is to have i=1 and deftime=time.ctime() available 
>>within foo, you could do something like (untested):
>>
>>class foo(object):
>>    def __init__(self):
>>        self.i = 1
>>        self.deftime = time.ctime()
>>    def __call__(self, x, y=123, *args, **kwds):
>>        return x*y, (kw.get('which_time') == 'now'
>>                     and time.ctime() or self.deftime)
>>foo = foo()
> 
> Total: 8 lines, much irrelevant cruft.
> 
> 
>>Or if you don't like 'foo = foo()', you could probably abuse the __new__ 
>>method (also untested):
>>
>>class foo(object):
>>    i = 1
>>    deftime = time.ctime()
>>    def __new__(cls, x, y=123, *args, **kwds):
>>        return x*y, (kw.get('which_time') == 'now'
>>                     and time.ctime() or self.deftime)
> 
> Total: 6 lines, newbie-unfriendly abuse of __new__ ;-)
> 
> 
>>I guess my point is that if you want attribute associated with the 
>>function, it's often easy enough to write a class instead...
> 
> vs. 2 easy lines with minimal irrelevant cruft ;-)

If you're really counting lines, you might compare with the more compact:

class foo(object):
     def __init__(self):
         self.i, self.deftime = 1, time.ctime()
     def __call__(self):
         return x*y, kw.get('which_time') == 'now' and time.ctime() or 
self.deftime
foo = foo()

and

class foo(object):
     i, deftime = 1, time.ctime()
     def __new__(cls, x, y=123, *args, **kwds):
         return x*y, kw.get('which_time') == 'now' and time.ctime() or 
self.deftime

And probably what you want to compare is N lines (function only) versus 
N + 2 lines (using __new__) and N + 4 lines (using __call__).  If N is 
more than 2 lines (which is probably true in most cases), the difference 
isn't so dramatic.

Using a class also has the advantage that, assuming one already knows 
how classes work (which any Python programmer who wants the kind of 
scoping you're talking about should) you don't have to learn how 
something new to be able to use it -- that is, *** -> bound locals that 
don't contribute to arguments but are listed in the same place as arguments.

If you really want locals that don't contribute to arguments, I'd be 
much happier with something like a decorator, e.g.[1]:

@with_consts(i=1, deftime=time.ctime())
def foo(x, y=123, *args, **kw):
     return x*y, kw.get('which_time')=='now' and time.ctime() or deftime

Then you don't have to mix parameter declarations with locals definitions.

Steve

[1] I have no idea how implementable such a decorator would be.  I'd 
just like to see function constants declared separate from arguments 
since they mean such different things.



More information about the Python-list mailing list