I am learning Python by doing Python Koans these days, and I came up with the problem of raising exceptions in Python. Specifically, I am even confused with try...except... after going through the koan. I know there is a similar question toward Rudy Koan Ruby Koan 151 raising exceptions. But I am a Python newbie and know nothing about Ruby.
So here is the koan:
# You need to finish implementing triangle() in the file 'triangle.py'
from triangle import *
class AboutTriangleProject2(Koan):
# The first assignment did not talk about how to handle errors.
# Let's handle that part now.
def test_illegal_triangles_throw_exceptions(self):
# Calls triangle(0, 0, 0)
self.assertRaises(TriangleError, triangle, 0, 0, 0)
self.assertRaises(TriangleError, triangle, 3, 4, -5)
self.assertRaises(TriangleError, triangle, 1, 1, 3)
self.assertRaises(TriangleError, triangle, 2, 4, 2)
the following is triangle.py
def triangle(a, b, c):
# DELETE 'PASS' AND WRITE THIS CODE
if a == b and b == c and c == a:
return 'equilateral'
if a == b or b == c or a == c:
return 'isosceles'
else:
return 'scalene'
# Error class used in part 2. No need to change this code.
class TriangleError(StandardError):
pass
I am supposed to finish triangle() function.
To my understanding, the try...except... functions like if certain criteria satisfied then do something, else through out an exception. Then in my situation, should I use if ... then raise TriangleError or try... except ...? What are the differences between them?
Thank you very much!
Please see the following answer:
def triangle(a, b, c):
# DELETE 'PASS' AND WRITE THIS CODE
if min([a,b,c])<=0:
raise TriangleError
x,y,z = sorted([a,b,c])
if x+y<=z:
raise TriangleError
if a==b and b==c and c==a:
return 'equilateral'
if a==b or b==c or a==c:
return 'isosceles'
else:
return 'scalene'
You want to use raise, which will cause an exception to be created and travel up the call stack until something handles the exception with a try/except block. For example, if you were using this triangle function in real code you would do something like
try:
t = triangle(x, y, z)
...
except TriangleError:
print 'Triangle', x, y, z, 'is no good!'
That way you'd be able to handle the error when a bad triangle was made and your program wouldn't crash.
here is my code,it worked
def triangle(a, b, c):
# DELETE 'PASS' AND WRITE THIS CODE
if min([a,b,c])<=0:
raise TriangleError,"no <0 numbers"
if sorted([a,b,c])[0]+sorted([a,b,c])[1]<=sorted([a,b,c])[2]:
raise TriangleError,"sf"
set_a=set(([a,b,c]))
if len(set_a)==1:
return 'equilateral'
elif len(set_a)==2:
return 'isosceles'
elif len(set_a)==3:
return 'scalene'
# Error class used in part 2. No need to change this code.
class TriangleError(StandardError):
pass
I ended up with something similar to nancy's answer, using set rather than comparing a,b and c manually, and it worked also.
def triangle(a, b, c):
# DELETE 'PASS' AND WRITE THIS CODE
ls = sorted([a,b,c])
if min(ls)<=0 or ls[0]+ls[1]<=ls[2]: raise TriangleError, 'Triángulo mal formado.'
l = len(list(set(ls)))
if(l==1): return 'equilateral'
if(l==2): return 'isosceles'
if(l==3): return 'scalene'
# Error class used in part 2. No need to change this code.
class TriangleError(StandardError):
pass
The better solution for the triangle.py file has been given below:
def triangle(a, b, c):
if a <= 0 or b <= 0 or c <= 0:
raise TriangleError(AttributeError('Negative edge is not allowed'))
if a + b + c <= 2 * max(a, b, c):
raise TriangleError(AttributeError('largest side should be smaller than sum of 2 other sides'))
if a == b == c:
return 'equilateral'
elif a == b or b == c or c == a:
return 'isosceles'
return 'scalene'
Error class used in part 2. No need to change this code.
class TriangleError(Exception):
pass
Related
I have the following code, which i know to be wrong and also know what exactly is wrong, lack of 'return' before both iterative calls:
alpha = 546
beta = 66
def Euclid(a, b):
while not (a == 0 or b == 0):
if a >= b:
Euclid(a % b, b)
else:
Euclid(a, b % a)
return max(a, b)
print(Euclid(alpha, beta))
My question is why does the code above not still work? I viewed it in code visualiser, and it shows that the last value it returns is indeed max(a,b), so GCD, but then after the function keeps executing the code forever, going from arguments of (6,12) to (6,0), and then starting all over again with (6,12), after returning (6,0). Would just like to know what exactly is happening, since i thought function always quits after the last return.
Let's take a look at your Euclid function and try to understand what happens line by line (and also pay close attention to the values of the variables):
def Euclid(a, b):
while not (a == 0 or b == 0):
if a >= b:
Euclid(a % b, b)
else:
Euclid(a, b % a)
return max(a, b)
You call it with a = 546 and b = 66. (a=546 and b=66)
You enter the while loop because a!=0 and b!=0 (a=546 and b=66)
Since a>=b you call the Euclid function with a%b and b (a=546 and b=66)
You ignore the return value (a=546 and b=66) (actually this call never returns either, but let's assume for now it does)
You go back to the start of your while loop and start again (a=546 and b=66)
The problem is that you never modify the values of a and b, so your while condition will always be true, and your first call of the function never reaches the return.
Ps.: B Remmelzwaal is right about how to fix your code, I only explained what is happening, which makes you get stuck in an infinite loop. Based on his answer the correct code would be:
def Euclid(a, b):
while not (a == 0 or b == 0):
if a >= b:
return Euclid(a % b, b)
else:
return Euclid(a, b % a)
return max(a, b)
Ps2.: To clarify some misunderstanding, the while loop is un necessary in the code, since you would enter only once and return from it. The commands inside the while are never repeated.
The code which reflects better what is happening would be:
def Euclid(a, b):
if a!=0 and b!=0:
if a >= b:
return Euclid(a % b, b)
else:
return Euclid(a, b % a)
return max(a, b)
So in this case when you call the function with a = 546 and b = 66, the following is going to happen:
Euclid(546,66)
Euclid(18,66)
Euclid(18,12)
Euclid(6,12)
Euclid(6,0) --> This is not calling any more Euclids and returns 6
return 6
return 6
return 6
return 6
return 6
So the Euclid(6,0) gives you 6, the Euclid(6,12) returns the return value of Euclid(6,0), which was 6. And so on while you reach back to the Euclid(546,66).
You can imagine it as a matryoshka doll, every outer doll tells you the number which the inner doll told it. In this analogy the smallest inner doll is the Euclid(6,0) and every other doll just repeats it as it proceeds through the layers.
I've made example functions, I know that i will use meta_function only with arguments sum and mult
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(function):
if function == sum:
c = 1
print('SUM')
elif function == mult:
c = 2
print("MULT")
print(c)
meta_function(sum)
meta_function(mult)
Output:
SUM
1
MULT
2
PyCharm informing me that Local variable might be referenced before assignment. I know, if some other arguments will be taken except sum and mult, this will lead to error. What is the best practice to handle this sophisticated issue? Try - except? Am I use wright way to take another functions in meta_function?
Pycharm warns you because if the function isn't sum or mult, your code will crash.
You need to define c at the top, just in case, or add an else statement.
# I changed the name of sum_ in order to avoid shadowing with
# the python built-in function sum()
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
def meta_function(function):
c = 0 # if function isn't sum or mult, at least c exist
# here, "is" is better then "=="
if function is sum_:
c = 1
print('SUM')
# here, "is" is better then "=="
elif function is mult:
c = 2
print("MULT")
# Or you can do the else, but since you don't do anything in it,
# the best solution is to define a base state of c
# else:
# c = 0
print(c)
meta_function(sum_)
meta_function(mult)
Here is a better solution in my opinion, in order to prevent changing the core of meta_function() if you want to add more functions:
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
function_switch = {
sum_: ('SUM', 1),
mult: ('MULT', 2),
}
def meta_function(function):
function_res = function_switch.get(function)
if function_res is None:
print(0)
return
print(function_res[0])
print(function_res[1])
meta_function(sum_)
meta_function(mult)
In Python 3.9, you can do:
def meta_function(function):
if (function_res := function_switch.get(function)) is None:
print(0)
return
print(function_res[0])
print(function_res[1])
Warning is shown because if both cases are not happened, then variable c will be not defined and print fill fail. The variable must be defined in the current scope.
It is better to make a generic function, executes the function and returns desired values. I've refactored your function as taking callable function and parameters for given function. Executes given callable and return some string that is built with return results.
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(func, *args):
# initialize default variables
c: int = None
return_str: str = None
# try execute given callable `func`
try:
# execute
c = func(*args)
# build some result string and assign
return_str = f"Function:{func.__name__}, Args: {args}, Result: {c}"
except Exception as exp:
# build result string for failed functions
return_str = f"Error calling given function `{func.__name__}` with params: {params}"
return return_str
def incompatible_func(x, y, z): # takes three parameters, will fail
return x + y * z
params = (1,2)
rslt = meta_function(sum, *params)
print(rslt)
rslt = meta_function(mult, *params)
print(rslt)
rslt = meta_function(incompatible_func, *params)
print(rslt)
And the output is:
Function:sum, Args: (1, 2), Result: 3
Function:mult, Args: (1, 2), Result: 2
Error calling given function `incompatible_func` with params: (1, 2)
def subtract1(a, b):
return a - b
if random.randint(0,100) < 99:
print("Yes")
print(subtract(20, 10)
I have tried many different ways of using a random number gen but can't seem to find one that works. I am now quite sure that it has to do something with the other code but I'm not sure what.
When you call your function in the print statement you have [print(subtract(number1, number2)]. Your function is named subtract1 so try to call subtract1 instead of subtract as the function name. Plus the return statement is stopping it before it reaches the if statement.
editing the function as the following way will solve your problem
Note that for any function the return statement always ends the function
and any other lines written in the function body after the return statement will never be compiled. also you call the function with the same name of its defination
import random
def subtract(a, b):
if random.randint(0,100) < 99:
print("Yes")
return a - b
print(subtract(20, 10))
By calling return immediately at the beginning of the function it returns the value you passed (a-b) and won't pass to the rest of the code.
if you want to print a-b before running the code below you can look at yield:
import random
def subtract(a,b):
yield a-b
if random.randint(1,100) < 99: print("yes")
print(subtract(20,10))
I can't understand what's your intention but if what I'm thinking is right,
your code should be like this
def subtract1(a, b):
if random.randint(0,100) < 99:
print("Yes")
return a - b
print(subtract(20, 10))
return statement returns the return value and at the same time ends the function.
So if it were for you to print the subtracted value first and print Yes later,
it has to be like this
def subtract1(a, b):
return a - b
print(subtract(20, 10))
if random.randint(0,100) < 99:
print("Yes")
Here is the code:
def smart_divide(func):
def inner(a,b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
else:
return func(a,b)
return inner
#smart_divide
def divide(c,d):
return c/d
divide(5,2)
This is a program to divide two numbers and give an error message if the denominator is 0.
We are passing 5 and 2 as an argument to the function divide. How does inner function know that the value of a and b is 5 and 2 respectively?
According to what I read on internet : #smart_divide means that divide = smart_divide(divide(c,d))
The function divide has parameters c,d while the function inner has parameters a,b. How does the interpreter know that a = c and b = d?
#smart_divide means that
result = smart_divide(divide)(c,d)
That means, divide no points to inner function, and inner function calls the original function divide via func.
divide = smart_divide(divide(c,d)) is not really a good description of what happens. Have a look at the logical expansion step by step, in some kind of pseudo-Python:
divide(x, y)
= smart_divide(lambda c, d: c / d)(x, y)
= inner[func ~> lambda c, d: c / d](x, y)
= (def inner(a,b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
else:
return (lambda c, d: c / d)(a, b))(x, y)
= print("I am going to divide",a,"and",b)
if y == 0:
print("Whoops! cannot divide")
else:
return (lambda c, d: c / d)(x, y))
= print("I am going to divide",a,"and",b)
if y == 0:
print("Whoops! cannot divide")
else:
return x / y
The important thing that happens is lexical scoping. If you substitute an expression in another expression (the ~> operation above), then you assume that the names don't get mangled. That is, (lambda a: lambda b: a)(b)) does not become lambda b: b, but (something equivalent to) lambda b_prime: b. Names don't matter: this is called alpha equivalence.
Your understanding of decorators is almost right, this should clarify things.
The code in your example:
#smart_divide
def divide(c,d):
return c/d
Is functionally identical to:
def simple_div(c,d):
return c/d
divide = smart_divide(simple_div)
divide is defined as the inner of the smart_divide decorator.
smart_divide is given simple_div as a parameter (func), which is what inner uses to call the originally decorated simple_div method.
Furthermore: smart_divide can be used to decorate multiple functions because a new inner function is created each time smart_divide is called, so the func reference is different each time.
I have multiple expensive functions that return results. I want to return a tuple of the results of all the checks if all the checks succeed. However, if one check fails I don't want to call the later checks, like the short-circuiting behavior of and. I could nest if statements, but that will get out of hand if there are a lot of checks. How can I get the short-circuit behavior of and while also storing the results for later use?
def check_a():
# do something and return the result,
# for simplicity, just make it "A"
return "A"
def check_b():
# do something and return the result,
# for simplicity, just make it "B"
return "B"
...
This doesn't short-circuit:
a = check_a()
b = check_b()
c = check_c()
if a and b and c:
return a, b, c
This is messy if there are many checks:
if a:
b = check_b()
if b:
c = check_c()
if c:
return a, b, c
Is there a shorter way to do this?
Just use a plain old for loop:
results = {}
for function in [check_a, check_b, ...]:
results[function.__name__] = result = function()
if not result:
break
The results will be a mapping of the function name to their return values, and you can do what you want with the values after the loop breaks.
Use an else clause on the for loop if you want special handling for the case where all of the functions have returned truthy results.
Write a function that takes an iterable of functions to run. Call each one and append the result to a list, or return None if the result is False. Either the function will stop calling further checks after one fails, or it will return the results of all the checks.
def all_or_none(checks, *args, **kwargs):
out = []
for check in checks:
rv = check(*args, **kwargs)
if not rv:
return None
out.append(rv)
return out
rv = all_or_none((check_a, check_b, check_c))
# rv is a list if all checks passed, otherwise None
if rv is not None:
return rv
def check_a(obj):
...
def check_b(obj):
...
# pass arguments to each check, useful for writing reusable checks
rv = all_or_none((check_a, check_b), obj=my_object)
In other languages that did have assignments as expressions you would be able to use
if (a = check_a()) and (b = check_b()) and (c = check_c()):
but Python is no such language. Still, we can circumvent the restriction and emulate that behaviour:
result = []
def put(value):
result.append(value)
return value
if put(check_a()) and put(check_b()) and put(check_c()):
# if you need them as variables, you could do
# (a, b, c) = result
# but you just want
return tuple(result)
This might loosen the connection between the variables and function calls a bit too much, so if you want to do lots of separate things with the variables, instead of using the result elements in the order they were put in the list, I would rather avoid this approach. Still, it might be quicker and shorter than some loop.
You could use either a list or an OrderedDict, using a for loop would serve the purpose of emulating short circuiting.
from collections import OrderedDict
def check_a():
return "A"
def check_b():
return "B"
def check_c():
return "C"
def check_d():
return False
def method1(*args):
results = []
for i, f in enumerate(args):
value = f()
results.append(value)
if not value:
return None
return results
def method2(*args):
results = OrderedDict()
for f in args:
results[f.__name__] = result = f()
if not result:
return None
return results
# Case 1, it should return check_a, check_b, check_c
for m in [method1, method2]:
print(m(check_a, check_b, check_c))
# Case 1, it should return None
for m in [method1, method2]:
print(m(check_a, check_b, check_d, check_c))
There are lots of ways to do this! Here's another.
You can use a generator expression to defer the execution of the functions. Then you can use itertools.takewhile to implement the short-circuiting logic by consuming items from the generator until one of them is false.
from itertools import takewhile
functions = (check_a, check_b, check_c)
generator = (f() for f in functions)
results = tuple(takewhile(bool, generator))
if len(results) == len(functions):
return results
Another way to tackle this is using a generator, since generators use lazy evaluation. First put all checks into a generator:
def checks():
yield check_a()
yield check_b()
yield check_c()
Now you could force evaluation of everything by converting it to a list:
list(checks())
But the standard all function does proper short cut evaluation on the iterator returned from checks(), and returns whether all elements are truthy:
all(checks())
Last, if you want the results of succeeding checks up to the failure you can use itertools.takewhile to take the first run of truthy values only. Since the result of takewhile is lazy itself you'll need to convert it to a list to see the result in a REPL:
from itertools import takewhile
takewhile(lambda x: x, checks())
list(takewhile(lambda x: x, checks()))
main logic:
results = list(takewhile(lambda x: x, map(lambda x: x(), function_list)))
if len(results) == len(function_list):
return results
you can learn a lot about collection transformations if you look at all methods of an api like http://www.scala-lang.org/api/2.11.7/#scala.collection.immutable.List and search/implement python equivalents
logic with setup and alternatives:
import sys
if sys.version_info.major == 2:
from collections import imap
map = imap
def test(bool):
def inner():
print(bool)
return bool
return inner
def function_for_return():
function_list = [test(True),test(True),test(False),test(True)]
from itertools import takewhile
print("results:")
results = list(takewhile(lambda x:x,map(lambda x:x(),function_list)))
if len(results) == len(function_list):
return results
print(results)
#personally i prefer another syntax:
class Iterator(object):
def __init__(self,iterable):
self.iterator = iter(iterable)
def __next__(self):
return next(self.iterator)
def __iter__(self):
return self
def map(self,f):
return Iterator(map(f,self.iterator))
def takewhile(self,f):
return Iterator(takewhile(f,self.iterator))
print("results2:")
results2 = list(
Iterator(function_list)
.map(lambda x:x())
.takewhile(lambda x:x)
)
print(results2)
print("with additional information")
function_list2 = [(test(True),"a"),(test(True),"b"),(test(False),"c"),(test(True),"d")]
results3 = list(
Iterator(function_list2)
.map(lambda x:(x[0](),x[1]))
.takewhile(lambda x:x[0])
)
print(results3)
function_for_return()
If you don't need to take an arbitrary number of expressions at runtime (possibly wrapped in lambdas), you can expand your code directly into this pattern:
def f ():
try:
return (<a> or jump(),
<b> or jump(),
<c> or jump())
except NonLocalExit:
return None
Where those definitions apply:
class NonLocalExit(Exception):
pass
def jump():
raise NonLocalExit()
Flexible short circuiting is really best done with Exceptions. For a very simple prototype you could even just assert each check result:
try:
a = check_a()
assert a
b = check_b()
assert b
c = check_c()
assert c
return a, b, c
except AssertionException as e:
return None
You should probably raise a custom Exception instead. You could change your check_X functions to raise Exceptions themself, in an arbitrary nested way. Or you could wrap or decorate your check_X functions to raise errors on falsy return values.
In short, exception handling is very flexible and exactly what you are looking for, don't be afraid to use it. If you learned somewhere that exception handling is not to be used for your own flow control, this does not apply to python. Liberal use of exception handling is considered pythonic, as in EAFP.
You mentioned 'short-circuiting' in your answer, which can be done with the 'or' statement. Top answer basically does the same thing, but in case someone wants to know more about this behaviour you could do this;
class Container(object):
def __init__(self):
self.values = []
def check_and_cache(self, value, checking_function):
value_true = checking_function(value)
if value_true:
self.values.append(value)
return True
c = Container()
if not c.check_and_cache(a, check_a) or not c.check_and_cache(b, check_b) or not c.check_and_cache(c, check_c):
print 'done'
return tuple(c.values)
The 'not .. or' setup of the if statements will result in a 'True' if the check fails, so the overall if statement passes without evaluating the remaining values.
Since I can not comment "wim":s answer as guest, I'll just add an extra answer.
Since you want a tuple, you should collect the results in a list and then cast to tuple.
def short_eval(*checks):
result = []
for check in checks:
checked = check()
if not checked:
break
result.append(checked)
return tuple(result)
# Example
wished = short_eval(check_a, check_b, check_c)
You can try use #lazy_function decorator from lazy_python
package. Example of usage:
from lazy import lazy_function, strict
#lazy_function
def check(a, b):
strict(print('Call: {} {}'.format(a, b)))
if a + b > a * b:
return '{}, {}'.format(a, b)
a = check(-1, -2)
b = check(1, 2)
c = check(-1, 2)
print('First condition')
if c and a and b: print('Ok: {}'.format((a, b)))
print('Second condition')
if c and b: print('Ok: {}'.format((c, b)))
# Output:
# First condition
# Call: -1 2
# Call: -1 -2
# Second condition
# Call: 1 2
# Ok: ('-1, 2', '1, 2')
This is similar to Bergi's answer but I think that answer misses the point of wanting separate functions (check_a, check_b, check_c):
list1 = []
def check_a():
condition = True
a = 1
if (condition):
list1.append(a)
print ("checking a")
return True
else:
return False
def check_b():
condition = False
b = 2
if (condition):
list1.append(b)
print ("checking b")
return True
else:
return False
def check_c():
condition = True
c = 3
if (condition):
list1.append(c)
print ("checking c")
return True
else:
return False
if check_a() and check_b() and check_c():
# won't get here
tuple1 = tuple(list1)
print (tuple1)
# output is:
# checking a
# (1,)
Or, if you don't want to use the global list, pass a reference of a local list to each of the functions.
If the main objection is
This is messy if there are many checks:
if a:
b = check_b()
if b:
c = check_c()
if c:
return a, b, c
A fairly nice pattern is to reverse the condition and return early
if not a:
return # None, or some value, or however you want to handle this
b = check_b()
if not b:
return
c = check_c()
if not c:
return
# ok, they were all truthy
return a, b, c