From: Ethan Furman on
Steven D'Aprano wrote:
> On Fri, 02 Apr 2010 12:39:16 -0700, Patrick Maupin wrote:
>
>
>>On Apr 2, 2:38 pm, Ethan Furman <et...(a)stoneleaf.us> wrote:
>
> [...]
>
>>>Sounds like a personal preference issue, rather than a necessary /
>>>unnecessary issue -- after all, if you call that function a thousand
>>>times, only once is mongo not defined... clearly the exception. ;)
>>>
>>>~Ethan~
>>
>>Well, I think the whole discussion has basically been about personal
>>preference. OTOH, but if you call the function a few million times, you
>>might find the cost of try/except to be something that you would rather
>>not incur -- it might become a performance issue rather than a personal
>>choice issue.
>
>
>
> The cost of a try...except is *very* low -- about the same as a pass
> statement:
>
>
>>>>from timeit import Timer
>>>>t1 = Timer("pass", "")
>>>>t2 = Timer("try:\n pass\nexcept Exception:\n pass", "")
>>>>min(t2.repeat())/min(t1.repeat())
>
> 1.9227982449955801
>
>
> Actually catching the exception, on the other hand, is quite expensive:
>
>
>>>>t1 = Timer("len('')", "")
>>>>t2 = Timer("try:\n len(0)\nexcept Exception:\n pass", "")
>>>>min(t2.repeat())/min(t1.repeat())
>
> 10.598482743564809
>
>
> The heuristic I use is, if I expect the try block to raise an exception
> more than about one time in ten, I change to an explicit test. In this
> case, since the exception should only be raised once, and then never
> again, I would use a try...except block.

That was my reasoning as well, but when I timed it for one million runs
(so 1 instantiation, 999,999 simple calls), the __getattr__ time was .5
seconds, the try...execpt block was .6; at ten million it was 5 and 6.

At those rates, personal preference takes over, at least for me.

~Ethan~
From: Stephen Hansen on
On 2010-04-02 19:42:29 -0700, Ethan Furman said:

> Terry Reedy wrote:
>>> In<Xns9D4EC021DC8EAduncanbooth(a)127.0.0.1> Duncan
>>> Booth<duncan.booth(a)invalid.invalid> writes:
>>>
>>>> class Spam(object):
>>>> mongo = None
>>>> def __call__(self, x, y, z):
>>>> if self.mongo is None:
>>>> self.mongo = heavy_lifting_at_runtime()
>>>> return frobnicate(x, y, z, self.mongo)
>>
>>
>> Unless one wants the intialization of mongo delayed in case spam is
>> never called, it can go in __init__ instead.
>
> As a matter of fact, I have an object that is usually not called during
> it's modules use, so I put in __getattr__. Sped the modules load time
> back up to pert near instantaneous. :)
>
> ~Ethan~

I prefer:

class Spam(object):
def __init__(self):
self._mondo = None

def _get_mondo(self):
if self._mondo is None:
self._mondo = heavy_lifting_at_runtime()
return self._mondo

mondo = property(_get_mondo)

def __call__(self, x, y, z):
return frobnicate(x,y,z, self.mondo)

I don't know if properties are really faster or slower then a
__getattr__, but I find them a lot cleaner if I want to delay some
calculation until needed like that.

--
--S

.... p.s: change the ".invalid" to ".com" in email address to reply privately.

From: Patrick Maupin on
On Apr 2, 10:11 pm, Stephen Hansen <apt.shan...(a)gmail.invalid> wrote:
>
> I don't know if properties are really faster or slower then a
> __getattr__, but I find them a lot cleaner if I want to delay some
> calculation until needed like that.

Well, the relative speed of properties vs. __getattr__ can become
irrelevant in at least two ways:

1) If the __getattr__ only calculates the value one time and then
stuffs it into the instance dictionary, now you are really comparing
the relative speed of properties vs. lookup of an attribute in the
instance dict. If you're at all concerned about speed, I think there
is a clear winner here.

2) There is a single __getattr__ function, vs. one property for every
attribute that needs a property. In cases where you can somehow
easily compute the attribute names as well as the attribute values,
__getattr__ can be a *lot* less code than defining dozens of
properties.

But you're absolutely right that, in many cases, property is the best
way to go for readability (especially if the property is read-only and
you're using a recent enough python to use decorators).

Regards,
Pat
From: Steven D'Aprano on
On Fri, 02 Apr 2010 19:48:59 -0700, Ethan Furman wrote:

>> The heuristic I use is, if I expect the try block to raise an exception
>> more than about one time in ten, I change to an explicit test. In this
>> case, since the exception should only be raised once, and then never
>> again, I would use a try...except block.
>
> That was my reasoning as well, but when I timed it for one million runs
> (so 1 instantiation, 999,999 simple calls), the __getattr__ time was .5
> seconds, the try...execpt block was .6; at ten million it was 5 and 6.

Care to share your timing code? Not that I don't trust your results, but
timings are very sensitive to the exact thing you do, and I'd like to see
what that is.




--
Steven
From: Ethan Furman on
Steven D'Aprano wrote:
> On Fri, 02 Apr 2010 19:48:59 -0700, Ethan Furman wrote:
>
>
>>>The heuristic I use is, if I expect the try block to raise an exception
>>>more than about one time in ten, I change to an explicit test. In this
>>>case, since the exception should only be raised once, and then never
>>>again, I would use a try...except block.
>>
>>That was my reasoning as well, but when I timed it for one million runs
>>(so 1 instantiation, 999,999 simple calls), the __getattr__ time was .5
>>seconds, the try...execpt block was .6; at ten million it was 5 and 6.
>
>
> Care to share your timing code? Not that I don't trust your results, but
> timings are very sensitive to the exact thing you do, and I'd like to see
> what that is.

Happy to do so -- if I made a mistake I'd like to know about it and learn.

It'll have to wait two days 'til I get back to work, though... I'll post
it asap.

~Ethan~