Your falseposition
function only explicitely returns when it finds a value of c
that meets the criterion before i
gets greater than imax
. If it doesn't, then it falls out of the while loop, reaches the end of the function body and then (implicitely) returns None
. When that's the case, the root, steps = falseposition(f, 1, 8)
statement effectively becomes root, steps = None
, and since None
is not iterable you get this error.
You mainly have two solutions here, either:
return None, i
as default at the end of the function (with None
signaling the function failed to found a matching value for c
):
def falseposition(f, a, b, imax=50, tolerance=1.0e-10):
i = 0
while i < imax:
c = (a*f(b)-b*f(a))/(f(b) - f(a))
if c == 0 or f(c) < tolerance:
return c, i
elif (f(a)*f(c))<0:
b=c
else:
a=c
i += 1
# nothing found
return None, i
or raise some exception instead (and let the caller code catch it):
def falseposition(f, a, b, imax=50, tolerance=1.0e-10):
i = 0
while i < imax:
c = (a*f(b)-b*f(a))/(f(b) - f(a))
if c == 0 or f(c) < tolerance:
return c, i
elif (f(a)*f(c))<0:
b=c
else:
a=c
i += 1
# nothing found
raise ValueError("No matching c value")
Which of those solutions makes sense depends on the context and whatnot, but since obviously there might be quite a few cases where no proper "solution" will be found the first one (returning (None
, i)`) seems a good candidate.
You could of course leave the function as is and make sure you test the return value before trying to unpack it:
result = falseposition(f, 1, 8)
if result is None:
print("oops, nothing found")
root, steps = result
# etc
but that's really really ugly, and bad practice actually (one expects the return value of a function to be of consistant type).