简体   繁体   中英

How to catch RecursionError in Python?

I am testing a function to find the root of a mathematical function in Python and if there is no root on the interval, a RecursionError is raised. However, when I try to handle the exception with a try-except block, it isn't raised anymore. What am I missing?

import math
sgn = lambda x: math.copysign(1, x)

def bisect(f, a, b, eps=1e-6):
    """
    Find an approximate root of f in the interval [a, b].

    Parameters
    ----------
    f : function
        A mathematical function, defined and with root on [a, b]
    a, b : numbers
        Endpoints of the interval. a < b.
    eps : number, optional, default 1e-6
        Tolerance of error.

    Returns
    -------
    x : number
        Approximate root of f on [a, b]
    """
    if a > b:
        raise ValueError("`b` needs to be greater than `a` for the interval [a, b] to exist.")
    x = (a + b) / 2
    if abs(f(x)) < eps:
        return x
    try:
        if sgn(f(a)) != sgn(f(x)):
            return bisect(f, a, x, eps)
        else:
            return bisect(f, x, b, eps)
    except RecursionError as e:
        raise RecursionError(f"There seems to be no root of f on the interval [{a}, {b}]")

def test_bisect():
    """Verify `bisect` using `f` = cos(pi * x), `a` = 0 `b` = 0.82 and `eps` = 1e-12."""
    from math import cos, pi
    f = lambda x: cos(pi * x)
    a = 0
    b = 0.4
    eps = 1e-12
    expected = 0.5
    computed = bisect(f, a, b, eps)
    error = abs(expected - computed)
    tolerance = 1e-12
    success = error < tolerance
    msg = f"""
    Error testing `piecewise`.
    Expected:  {expected}
    Computed:  {computed}
    Error:     {error:13.6e}
    Tolerance: {tolerance:13.6e}"""
    assert success, msg

test_bisect()

I think you had the try/except in the wrong place. I moved it to inside the test_bisect() function.

import math

sgn = lambda x: math.copysign(1, x)

def bisect(f, a, b, eps=1e-6):
    """
    Find an approximate root of f in the interval [a, b].

    Parameters
    ----------
    f : function
        A mathematical function, defined and with root on [a, b]
    a, b : numbers
        Endpoints of the interval. a < b.
    eps : number, optional, default 1e-6
        Tolerance of error.

    Returns
    -------
    x : number
        Approximate root of f on [a, b]
    """
    if a > b:
        raise ValueError("`b` needs to be greater than `a` for the interval [a, b] to exist.")
    x = (a + b) / 2
    if abs(f(x)) < eps:
        return x
    if sgn(f(a)) != sgn(f(x)):
        return bisect(f, a, x, eps)
    else:
        return bisect(f, x, b, eps)

def test_bisect():
    """Verify `bisect` using `f` = cos(pi * x), `a` = 0 `b` = 0.82 and `eps` = 1e-12."""
    from math import sin, pi, cos
    f = lambda x: cos(pi * x)
    a = 0
    b = 0.4
    eps = 1e-12
    expected = 0.5
    try:
        computed = bisect(f, a, b, eps)
    except RecursionError as e:
        print(f"There seems to be no root of f on the interval [{a}, {b}]")
        return

    error = abs(expected - computed)
    tolerance = 1e-12
    success = error < tolerance
    msg = f"""
    Error testing `piecewise`.
    Expected:  {expected}
    Computed:  {computed}
    Error:     {error:13.6e}
    Tolerance: {tolerance:13.6e}"""
    assert success, msg

test_bisect()

I found the problem.. It seems the exception is not raised when using a Jupyter Notebook. I mean it is raised without the try-except block, but with it, it only works from the command line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM