Guarding arithmetic

M

Mark Carter

Suppose I want to define a function "safe", which returns the argument passed if there is no error, and 42 if there is one. So the setup is something like:

def safe(x):
# WHAT WOULD DEFINE HERE?

print safe(666) # prints 666
print safe(1/0) # prints 42

I don't see how such a function could be defined. Is it possible?
 
C

Chris Angelico

Suppose I want to define a function "safe", which returns the argument passed if there is no error, and 42 if there is one. So the setup is something like:

def safe(x):
# WHAT WOULD DEFINE HERE?

print safe(666) # prints 666
print safe(1/0) # prints 42

I don't see how such a function could be defined. Is it possible?

That can work ONLY if the division of 1/0 doesn't raise an exception.
This is why the concept of NaN exists; I'm not sure if there's a way
to tell Python to return NaN instead of bombing, but it's most likely
only possible with floating point, not integer.

However, there's an easier way.

try:
print 1/0
except ZeroDivisionError:
print 42

Catch the exception and do with it what you will.

ChrisA
 
M

Mark Carter

only possible with floating point, not integer.

try:
print 1/0
except ZeroDivisionError:
print 42

OK, so it looks like a solution doesn't exist to the problem as specified. I guess it's something that only a language with macros could accommodate.
 
M

Mark Carter

only possible with floating point, not integer.

try:
print 1/0
except ZeroDivisionError:
print 42

OK, so it looks like a solution doesn't exist to the problem as specified. I guess it's something that only a language with macros could accommodate.
 
L

Laszlo Nagy

Suppose I want to define a function "safe", which returns the argument passed if there is no error, and 42 if there is one. So the setup is something like:

def safe(x):
# WHAT WOULD DEFINE HERE?

print safe(666) # prints 666
print safe(1/0) # prints 42

I don't see how such a function could be defined. Is it possible?
You are very vague. "There is an error" - but what kind of error? To
catch all possible exceptions you could do:

def unsafe(x):
# put your code here...

def safe(x):
try:
return unsafe(x)
except:
return 42

Generally, it is a bad idea. Exception handlers were invented because
they give you a way to handle any error in the call chain. When an
exception occurs, the interpreter will start searching for an
appropriate exception handler traversing up in the call chain. By
converting exceptions into return values, you are bypassing this search.
Then you will have to write conditions instead of exception handlers
inside ALL methods in the call chain, creating a "manual" search for the
handler of the exception. In most cases, this will make your code
difficult, error prone and hard to read.

In some special cases, this can be a good idea to do.

Can you please let us know when and how would you like to use it?
 
L

Laszlo Nagy

That can work ONLY if the division of 1/0 doesn't raise an exception.
This is why the concept of NaN exists; I'm not sure if there's a way
to tell Python to return NaN instead of bombing, but it's most likely
only possible with floating point, not integer.
For integers, Python will always raise an exception when you try to
divide by zero. And integers has nothing to do with NaN. Because NaN is
meaningful for floating point numbers only. Python can be compiled to
raise floating point exceptions. (On Python 2, this is a compile time
option: FPECTL. On Python 3, this can be configured runtime:
http://docs.python.org/library/fpectl.html )
 
C

Chris Angelico

OK, so it looks like a solution doesn't exist to the problem as specified. I guess it's something that only a language with macros could accommodate.

You're asking for a function to prevent the evaluation of its
arguments from throwing an error. That's fundamentally not possible
with a function call. Exception handling is a much more normal way of
handling it; though if you prefer, you could wrap it up in a function
like this:

def safe_div(num,denom):
try:
return num/denom
except ZeroDivisionError:
return 42

print safe_div(1,0) # still a poor name though

ChrisA
 
C

Chris Angelico

For integers, Python will always raise an exception when you try to divide
by zero. And integers has nothing to do with NaN. Because NaN is meaningful
for floating point numbers only. Python can be compiled to raise floating
point exceptions. (On Python 2, this is a compile time option: FPECTL. On
Python 3, this can be configured runtime:
http://docs.python.org/library/fpectl.html )

Thanks, that's the sort of thing I meant. I'm not familiar enough with
Python's floating point handler to know those details.

ChrisA
 
P

Peter Otten

Mark said:
Suppose I want to define a function "safe", which returns the argument
passed if there is no error, and 42 if there is one. So the setup is
something like:

def safe(x):
# WHAT WOULD DEFINE HERE?

print safe(666) # prints 666
print safe(1/0) # prints 42

I don't see how such a function could be defined. Is it possible?

1/0 is evaluated before safe() is called. Therefore safe() has no chance to
catch the exception. You have to move the evaluation into the safe()
function:
.... try:
.... return deferred()
.... except exception:
.... return default
....42
 
L

Laszlo Nagy

... try:
... return deferred()
... except exception:
... return default

What a beautiful solution! I was wondering if the following would be
possible:


def test(thing, default, *exc_classes):
try:
thing()
except *exc_classes:
return default


But it is syntactically invalid.

Here is a workaround that is not so beautiful:


def test(thing, default, *exc_classes):
try:
thing()
except Exception, e:
for cls in exc_classes:
if isinstance(e,cls):
return default
raise

print test( (lambda: 1/0), -1, ValueError, ZeroDivisionError) # prints -1
 
M

MRAB

What a beautiful solution! I was wondering if the following would be
possible:


def test(thing, default, *exc_classes):
try:
thing()
except *exc_classes:
return default


But it is syntactically invalid.

Here is a workaround that is not so beautiful:


def test(thing, default, *exc_classes):
try:
thing()
except Exception, e:
for cls in exc_classes:
if isinstance(e,cls):
return default
raise

print test( (lambda: 1/0), -1, ValueError, ZeroDivisionError) # prints -1
The 'except' clause accepts a tuple of exception classes, so this works:

def test(thing, default, *exc_classes):
try:
thing()
except exc_classes:
return default
 
P

Peter Otten

Laszlo said:
What a beautiful solution! I was wondering if the following would be
possible:


def test(thing, default, *exc_classes):
try:
thing()
except *exc_classes:
return default


But it is syntactically invalid.

The except clause allows a tuple of exceptions:
.... try:
.... return deferred()
.... except exceptions:
.... return default
....-1
 
M

Mark Lawrence

Suppose I want to define a function "safe", which returns the argument passed if there is no error, and 42 if there is one. So the setup is something like:

def safe(x):
# WHAT WOULD DEFINE HERE?

print safe(666) # prints 666
print safe(1/0) # prints 42

I don't see how such a function could be defined. Is it possible?

Well you've already got lots of answers but I'm not certain about what
you're trying to achieve. If you could explicitly state your
requirements I'm sure that the numerous MVPs (Most Valuable Pythonistas)
here would come up with The Best Solution ™ to your problem.
 
R

rusi

1/0 is evaluated before safe() is called. Therefore safe() has no chance to
catch the exception. You have to move the evaluation into the safe()
function:


...     try:
...             return deferred()
...     except exception:
...             return default
...>>> print safe(lambda: 666)
666

42

Nice!

Functional programmers know that once you have a lazy language, you
can simulate all control constructs within that framework. eg in
Haskell one could define an 'if-function' as
iffunc (True, a, b) = a
iffunc (False, a, b) = b
In most other languages such a definition would not work.

What Peter has demonstrated is that for an eager language like python,
a bare-lambda is a lazy-fying construct
 
D

Dennis Lee Bieber

print safe(1/0) # prints 42

1/0 is evaluated before safe() is called... and hence will raise an
exception before getting into the function.

Python does not defer operations that are arguments of a function
call. You can only defer them by wrapping them into a function...
.... try:
.... return arg()
.... except:
.... return 42
....42

Unfortunately, that means you can't just take an arbitrary equation
and wrap it in a call to safe() as you have to make provision for local
names...
.... try:
.... return fnc(*args)
.... except:
.... return 42
....
By the time you wrap the equation with a lambda all, named, terms,
AND supply the named terms after the lambda, you might as well just wrap
the equation in a plain try/except block.
 
C

Chris Angelico

By the time you wrap the equation with a lambda all, named, terms,
AND supply the named terms after the lambda, you might as well just wrap
the equation in a plain try/except block.

But closures spare you that hassle.
0.5

(Example done in Python 3 so semantics are a little different)

ChrisA
 

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

No members online now.

Forum statistics

Threads
474,145
Messages
2,570,826
Members
47,373
Latest member
Desiree036

Latest Threads

Top