Feature suggestion -- return if true

W

Westley Martínez

I think I see your point -- the OP said:
--> _temp = expr
--> if _temp: return _temp

which is where you're getting
--> return _temp or None

However, most everyone ('cept you, it seems! ;) understood that there
would be more lines of code such that if bool(expr) == False no return
is executed and the function keeps going merrily along. Like this:
--> def func():
--> var1 = something()
--> var2 = something_else('this')
--> return? var1.hobgle(var2)
--> var3 = last_resort(var1)
--> return var3.wiglat(var2)


Looking back at the whole post now, I see nothing there to concretely
make that point except the question mark on the return.

Hope this helps.

~Ethan~

The question mark makes the programmer look like he wasn't sure of what
he was doing at the time. "Hmm, should I return this object or not?"
 
E

Ethan Furman

Westley said:
The question mark makes the programmer look like he wasn't sure of what
he was doing at the time. "Hmm, should I return this object or not?"

Yeah, I'm definitely -1 on the ?, as well as -1 on the idea. All other
major flow control uses indentation, and this does not.

~Ethan~
 
S

Steven D'Aprano

Yeah, I'm definitely -1 on the ?, as well as -1 on the idea. All other
major flow control uses indentation, and this does not.

Neither return nor raise use indentation, and you don't get much more
major than those. Nor do list comps or generator expressions.

Indentation is not appropriate here because it doesn't involve a block of
code. The whole point is that it just involves a single expression.

But in any case, I'm -1 on any syntax involving ? in Python, and +0 on
the concept on a conditional return. I suspect it's too specialised to be
worth special syntax or a keyword, but I can definitely see some uses for
it.
 
E

Ethan Furman

Steven said:
Neither return nor raise use indentation, and you don't get much more
major than those. Nor do list comps or generator expressions.

The indentation for return and raise is the next coded line. List comps
and gen exps are basically uber-functions, and regardless of how you
categorize them when they finish it is easy to see where control goes to
next because of indentation. With this idea flow can leave the function
with no indentation clue, making it easy to miss (assuming, of course,
it's not some bright syntax highlighted color).

~Ethan~
 
C

Chris Angelico

The indentation for return and raise is the next coded line.  List comps and
gen exps are basically uber-functions, and regardless of how you categorize
them when they finish it is easy to see where control goes to next because
of indentation.  With this idea flow can leave the function with no
indentation clue, making it easy to miss (assuming, of course, it's not some
bright syntax highlighted color).

So if I have a function that can early-abort, I should indent after that?

def foo(param):
resource=malloc(50000) # Shtarker, zis is Python! We don't malloc here!
if not resource: return 0
resource[param]=5
del resource
return 1

Now, this pattern of "attempt to acquire resource, return if unable
to" is probably better recoded as "acquire resource and have it throw
an error if it can't"; but if you're eyeballing for control-flow
changes, a called function throwing an error is even less obvious than
an if: return.

Where should the indentation go?

As I understand it, Python uses indents the way C uses braces - to
delimit blocks of code. The only reason to indent in foo() above would
be if the if has an else:

if not resource: return 0
else:
resource[param]=5
del resource
return 1

or, flipping that the other way around:

if resource:
resource[param]=5
del resource
return 1
return 0

but both of these are grossly inferior to:

def foo(param):
resource=generate_resource()
resource.dosomething(param,5)
return 1

However, what's to tell you that generate_resource() will throw a
KaosError if Siegfried is around?

(Oh, and apologies to all for picking a different comedy source for my
references. Sometimes even a python has to get smart...)

Chris Angelico
 
J

James Mills

def foo(param):
   resource=malloc(50000) # Shtarker, zis is Python! We don'tmalloc here!
   if not resource: return 0
   resource[param]=5
   del resource
   return 1

In Python this can probably be done and perhaps is slightly more
readble with the "with" statement and a context manager:

def foo(param):
try:
with ResourceAllocator(50000) as resource:
resource[param] = 5
return 1
except:
return 0

Now I know I'm only adding to the discussion (argument) but
hey it's all in good fun until someone looses an eyeball!

cheers
James
 
T

Teemu Likonen

* 2011-04-12T13:26:48-07:00 * Chris Rebert said:
I think Ben "Yahtzee" Croshaw's comments on open-world sandbox video
games (of all things) have a lot of applicability to why allowing
full-on macros can be a bad idea.
IOW, a language is usually better for having such discussions and
having a fairly coherent worldview enforced by the existence of a
managing authority.

Thanks for the info. That's a strange view, and I must disagree. Lisp
people certainly love the power they have. But different language,
different philosophy, I guess.
 
G

Gregory Ewing

Chris said:
def fac(n):
# attempt to get from a cache
return? cache[n]
# not in cache, calculate the value
ret=1 if n<=1 else fac(n-1)*n
# and cache and return it
cache[n]=ret; return ret

My idiom for fetching from a cache looks like this:

def get_from_cache(x):
y = cache.get(x)
if not y:
y = compute_from(x)
cache[x] = y
return y

which doesn't require any conditional returns.
 
C

Chris Angelico

My idiom for fetching from a cache looks like this:

 def get_from_cache(x):
   y = cache.get(x)
   if not y:
     y = compute_from(x)
     cache[x] = y
   return y

which doesn't require any conditional returns.

There's not a lot of difference between conditionally returning and
conditionally executing all the code between here and the return,
except that when you string three conditional returns together by your
method, it gets three indentations.

Chris Angelico
 
S

Steven D'Aprano

Chris said:
def fac(n):
# attempt to get from a cache
return? cache[n]
# not in cache, calculate the value
ret=1 if n<=1 else fac(n-1)*n
# and cache and return it
cache[n]=ret; return ret

My idiom for fetching from a cache looks like this:

def get_from_cache(x):
y = cache.get(x)
if not y:
y = compute_from(x)
cache[x] = y
return y

which doesn't require any conditional returns.


I'm sure you realise that that snippet needlessly recalculates any cached
result that happens to be false, but others reading might not.

If the compute_from function is expensive (and it better be, otherwise
why are you bothering with a cache?), that could be expensive:

compute_from = is_prime_number
get_from_cache(253590421923456781012937340348512751108342137327 *
195789732345627381015532937340363481051277321451)

:)

A better way is to explicitly test for the sentinel:

y = cache.get(x)
if y is not None:
...
 
C

Chris Angelico

Chris said:
def fac(n):
    # attempt to get from a cache
    return? cache[n]
    # not in cache, calculate the value
    ret=1 if n<=1 else fac(n-1)*n
    # and cache and return it
    cache[n]=ret; return ret

My idiom for fetching from a cache looks like this:

   def get_from_cache(x):
     y = cache.get(x)
     if not y:
       y = compute_from(x)
       cache[x] = y
     return y

which doesn't require any conditional returns.


I'm sure you realise that that snippet needlessly recalculates any cached
result that happens to be false, but others reading might not.

Sure. In my (somewhat contrived) example of factorials, that's going
to be true (apart from 0! = 0); and if the function returns a string
or other object rather than an integer, same thing. If there's the
possibility of _ANY_ value coming back from the computation, then it
would need to be done as:

if x in cache: return cache[x]

or as:

try:
return cache[x]
except KeyError:
# calculate etc

Obviously, as with everything, you need to know your own code and your own data.

Chris Angelico
 
M

Martin v. Loewis

be expanded to
This could be simplified to just:

return expr or None
"""

No, it can't be simplified in this way.
If there is code after that snippet, then
it will get executed in the original version if _temp is
false, but won't get executed in your simplification.

Regards,
Martin
 
D

D'Arcy J.M. Cain

My idiom for fetching from a cache looks like this:

def get_from_cache(x):
y = cache.get(x)
if not y:
y = compute_from(x)
cache[x] = y
return y

I prefer not to create and destroy objects needlessly.

def get_from_cache(x):
if not x in cache:
cache[x] = compute_from(x)
return cache[x]
 
S

Steven D'Aprano

My idiom for fetching from a cache looks like this:

def get_from_cache(x):
y = cache.get(x)
if not y:
y = compute_from(x)
cache[x] = y
return y

I prefer not to create and destroy objects needlessly.

def get_from_cache(x):
if not x in cache:
cache[x] = compute_from(x)
return cache[x]

What object(s) do you think are being created and destroyed needlessly?

(This is not a rhetorical question.)
 
S

Steven D'Aprano

If there's the
possibility of _ANY_ value coming back from the computation, then it
would need to be done as:

if x in cache: return cache[x]

Or you can create a sentinel value that is guaranteed to never appear
anywhere else:


SENTINEL = object()
obj = cache.get(x, SENTINEL)
if obj is SENTINEL:
obj = calculate(x)
cache[x] = obj
return obj

You can create the sentinel once, at the start of your program, rather
than each time.
 
G

Greg Ewing

D'Arcy J.M. Cain said:
def get_from_cache(x):
y = cache.get(x)
if not y:
y = compute_from(x)
cache[x] = y
return y

I prefer not to create and destroy objects needlessly.

How does that create objects needlessly?
def get_from_cache(x):
if not x in cache:
cache[x] = compute_from(x)
return cache[x]

That looks up the cache *twice* for every access. Mine
only does one lookup when the item is already in the
cache.
 
J

James Mills

No, it can't be simplified in this way.
If there is code after that snippet, then
it will get executed in the original version if _temp is
false, but won't get executed in your simplification.

Martin, we've been over this! :) And you're a bit late...

You said it yourself "If there is code after that snippet"

cheers
James
 
G

Gregory Ewing

Steven said:
I'm sure you realise that that snippet needlessly recalculates any cached
result that happens to be false, but others reading might not.

I only use it as written when I'm dealing with types that
don't have false values. If I did need to cache such a
type, I would use a different test, such as 'if y is None'.
 
D

Dave Angel

<snip>

Sure. In my (somewhat contrived) example of factorials, that's going
to be true (apart from 0! = 0); and if the function returns a string
or other object rather than an integer, same thing. If there's the

Just to be pedantic, by any reasonable definition, 0! == one, not zero.

One reference: http://en.wikipedia.org/wiki/Factorial
3rd sentence.

More interestingly, There is a gamma function defined for lots of real
and complex numbers, which for all non-negative integers matches the
factorial:

gamma(n) = (n-1)!

The gamma function has the same value (1) for one and two, so to be
consistent, factorial should have that value for both zero and one.

DaveA
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top