Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > If/then style question

Reply
Thread Tools

If/then style question

 
 
Carl Banks
Guest
Posts: n/a
 
      12-19-2010
On Dec 17, 12:23*am, Steven D'Aprano <steve
(E-Mail Removed)> wrote:
> On Thu, 16 Dec 2010 20:32:29 -0800, Carl Banks wrote:
> > Even without the cleanup issue, sometimes you want to edit a function to
> > affect all return values somehow. *If you have a single exit point you
> > just make the change there; if you have mulitple you have to hunt them
> > down and change all of them--if you remember to. *I just got bit by that
> > one.

>
> If your function has so many exit points that you can miss some of them
> while editing, your function is too big, does too much, or both.


Sanctimonious much? In the real world, people "miss things" and "make
mistakes" and not necessarily because they are working on something
too complex to handle. It happens.


Carl Banks
 
Reply With Quote
 
 
 
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-19-2010
On Sat, 18 Dec 2010 19:59:45 -0800, Carl Banks wrote:

> On Dec 17, 12:23*am, Steven D'Aprano <steve
> (E-Mail Removed)> wrote:
>> On Thu, 16 Dec 2010 20:32:29 -0800, Carl Banks wrote:
>> > Even without the cleanup issue, sometimes you want to edit a function
>> > to affect all return values somehow. *If you have a single exit point
>> > you just make the change there; if you have mulitple you have to hunt
>> > them down and change all of them--if you remember to. *I just got bit
>> > by that one.

>>
>> If your function has so many exit points that you can miss some of them
>> while editing, your function is too big, does too much, or both.

>
> Sanctimonious much? In the real world, people "miss things" and "make
> mistakes" and not necessarily because they are working on something too
> complex to handle. It happens.



Really? I had no idea. I've never made a misteak, I asumed evrybody else
was equally brilliant. No, wait, there was that one time...

*wink*

Of course people make mistakes. So what's your point?

The point I was trying to make is that rather than encouraging an idiom
(only one return statement, even if the algorithm is more clearly written
with multiple exists) that leads to more complex, less efficient code
just in case you might someday need to modify the return result, there
are simple alternatives that avoid the need for anti-patterns like copy-
and-paste coding or enforced single exit point. I gave two:

- refactor the complex code so that it's less complex (e.g. instead of 20
exit points, which makes it easy to miss one or two, refactor it so there
are two or three exit points); or if that's not practical:

- wrap it in a decorator that performs the post-processing you need.

Both can be simple, effective and Pythonic. Neither require the coder to
use an artificial idiom just in case of some future need. The decorator
solution works even if you don't have access to the source code, or if
the function is a complex black box that nobody understands well enough
to touch.



--
Steven
 
Reply With Quote
 
 
 
 
Francesco
Guest
Posts: n/a
 
      12-21-2010
I'd bet you would stress your point Steven! But you don't need to persuade me, I do already agree.
I just meant to say that, when the advantage is little, there's no need to rewrite a working function.
And that with modern CPUs, if tests take so little time, that even some redundant one is not so much of a nuisance.
in your working example, the "payload" is just a couple of integer calculations, that take very little time too. So the overhead due
to redundant if tests does show clearly. And also in that not-really-real situation, 60% overhead just meant less than 3 seconds.
Just for the sake of discussion, I tried to give both functions some plough to pull, and a worst-case situation too:

>>> t1 = Timer('for x in range(100): print func1(0),',

.... 'from __main__ import func1')
>>>
>>> t2 = Timer('for x in range(100): print func2(0),',

.... 'from __main__ import func2')
>>>
>>> min(t1.repeat(number=1, repeat=1))

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
53.011015366479114
>>> min(t2.repeat(number=1, repeat=1))

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
47.55442856564332

that accounts for a scant 11% overhead, on more than one million tests per cycle.

That said, let's make really clear that I would heartily prefer func2 to func1, based both on readability and speed. Thank you for
having spent some time playing with me!
Francesco

On 19/12/2010 1.05, Steven D'Aprano wrote:
> Well, let's try it with a working (albeit contrived) example. This is
> just an example -- obviously I wouldn't write the function like this in
> real life, I'd use a while loop, but to illustrate the issue it will do.
>
> def func1(n):
> result = -1
> done = False
> n = (n+1)//2
> if n%2 == 1:
> result = n
> done = True
> if not done:
> n = (n+1)//2
> if n%2 == 1:
> result = n
> done = True
> if not done:
> n = (n+1)//2
> if n%2 == 1:
> result = n
> done = True
> if not done:
> for i in range(1000000):
> if not done:
> n = (n+1)//2
> if n%2 == 1:
> result = n
> done = True
> return result
>
>
> def func2(n):
> n = (n+1)//2
> if n%2 == 1:
> return n
> n = (n+1)//2
> if n%2 == 1:
> return n
> n = (n+1)//2
> if n%2 == 1:
> return n
> for i in range(1000000):
> n = (n+1)//2
> if n%2 == 1:
> return n
> return -1
>
>
> Not only is the second far more readable that the first, but it's also
> significantly faster:
>
>>>> from timeit import Timer
>>>> t1 = Timer('for i in range(20): x = func1(i)',

> ... 'from __main__ import func1')
>>>> t2 = Timer('for i in range(20): x = func2(i)',

> ... 'from __main__ import func2')
>>>> min(t1.repeat(number=10, repeat=5))

> 7.3219029903411865
>>>> min(t2.repeat(number=10, repeat=5))

> 4.530779838562012
>
> The first function does approximately 60% more work than the first, all
> of it unnecessary overhead.
>
>
>


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
DataGrid header style inconsistent with sortable column style cedoucette@alum.rpi.edu ASP .Net 0 10-14-2005 12:13 AM
All style tags after the first 30 style tags on an HTML page are not applied in Internet Explorer Rob Nicholson ASP .Net 3 05-28-2005 03:11 PM
Need help with Style conversion from Style object to Style key/value collection. Ken Varn ASP .Net Building Controls 0 04-26-2004 07:06 PM
Javascript Style Switcher that remebers current site style in use Hardeep Rakhra HTML 8 01-15-2004 08:00 PM
Style sheets, include one style within another (not inheritance) foldface@yahoo.co.uk HTML 1 11-24-2003 01:37 PM



Advertisments