标签归档:exception

异常后如何重试?

问题:异常后如何重试?

我有一个以开头的循环for i in range(0, 100)。正常情况下,它可以正常运行,但有时由于网络条件而失败。目前,我已对其进行了设置,以便在失败时,它将continue在except子句中(继续到的下一个数字i)。

我是否可以将相同的数字重新分配给i循环并再次执行失败的循环?

I have a loop starting with for i in range(0, 100). Normally it runs correctly, but sometimes it fails due to network conditions. Currently I have it set so that on failure, it will continue in the except clause (continue on to the next number for i).

Is it possible for me to reassign the same number to i and run through the failed iteration of the loop again?


回答 0

做一个while True内部的for循环,把你的try代码中,并突破从while只有当你的代码的成功循环。

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

Do a while True inside your for loop, put your try code inside, and break from that while loop only when your code succeeds.

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

回答 1

我更喜欢限制重试的次数,这样,如果该特定项目有问题,您最终将继续进行下一个,因此:

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

I prefer to limit the number of retries, so that if there’s a problem with that specific item you will eventually continue onto the next one, thus:

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

回答 2

重试包是重试的代码块失败的好办法。

例如:

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

The retrying package is a nice way to retry a block of code on failure.

For example:

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

回答 3

这是一个与其他解决方案类似的解决方案,但是如果未按规定的次数或重试次数失败,则会引发异常。

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

Here is a solution similar to others, but it will raise the exception if it doesn’t succeed in the prescribed number or retries.

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

回答 4

没有使用那些难看的while循环的更“实用”的方法:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

The more “functional” approach without using those ugly while loops:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

回答 5

最清晰的方法是显式设置i。例如:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

The clearest way would be to explicitly set i. For example:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

回答 6

带有超时的通用解决方案:

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

用法:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

A generic solution with a timeout:

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

Usage:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

回答 7

for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

我的版本与上述几种类似,但是没有使用单独的while循环,并且如果所有重试均失败,则会重新引发最新的异常。可以err = None在顶部显式设置,但不是严格必需的,因为只有在else出现错误并因此err被设置时才执行最后一个块。

for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

My version is similar to several of the above, but doesn’t use a separate while loop, and re-raises the latest exception if all retries fail. Could explicitly set err = None at the top, but not strictly necessary as it should only execute the final else block if there was an error and therefore err is set.


回答 8

Python Decorator库中有类似的东西

请记住,它不会测试异常,而是返回值。重试,直到修饰的函数返回True。

稍加修改的版本应该可以解决问题。

There is something similar in the Python Decorator Library.

Please bear in mind that it does not test for exceptions, but the return value. It retries until the decorated function returns True.

A slightly modified version should do the trick.


回答 9

使用递归

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

Using recursion

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

回答 10

使用while和计数器:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

Using while and a counter:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

回答 11

您可以使用Python重试包。 重试

它是用Python编写的,以简化将重试行为添加到几乎所有内容的任务。

You can use Python retrying package. Retrying

It is written in Python to simplify the task of adding retry behavior to just about anything.


回答 12

retryingtenacitybackoff(2020更新)的替代方案

重试库是以前的路要走,但可悲的是它有一些缺陷,自2016年其他选择似乎还没有得到任何更新补偿坚韧。在撰写本文时,坚韧程度更高的GItHub星星(2.3k和1.2k)并已更新,因此我选择使用它。这是一个例子:

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

上面的代码输出类似:

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

tenacity.retry韧度GitHub页面上列出了的更多设置。

Alternatives to retrying: tenacity and backoff (2020 update)

The retrying library was previously the way to go, but sadly it has some bugs and it hasn’t got any updates since 2016. Other alternatives seem to be backoff and tenacity. During the time of writing this, the tenacity had more GItHub stars (2.3k vs 1.2k) and was updated more recently, hence I chose to use it. Here is an example:

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

for i in range(100):
    do_something_unreliable(i)

The above code outputs something like:

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

More settings for the tenacity.retry are listed on the tenacity GitHub page.


回答 13

如果您想要一个没有嵌套循环且无需break成功调用的解决方案,则可retriable以为任何可迭代的对象开发一个快速包装。这是我经常遇到的网络问题的示例-保存的身份验证过期。它的使用将如下所示:

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

If you want a solution without nested loops and invoking break on success you could developer a quick wrap retriable for any iterable. Here’s an example of a networking issue that I run into often – saved authentication expires. The use of it would read like this:

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

回答 14

我在代码中使用以下代码,

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

I use following in my codes,

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

回答 15

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>

回答 16

这是我对这个问题的看法。以下retry功能支持以下功能:

  • 成功时返回被调用函数的值
  • 如果尝试已用尽,则引发被调用函数的异常
  • 尝试次数的限制(0表示无限制)
  • 尝试之间等待(线性或指数)
  • 仅当异常是特定异常类型的实例时才重试。
  • 可选的尝试记录
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

用法:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

有关更多信息,请参见我的帖子

Here is my take on this issue. The following retry function supports the following features:

  • Returns the value of the invoked function when it succeeds
  • Raises the exception of the invoked function if attempts exhausted
  • Limit for the number of attempts (0 for unlimited)
  • Wait (linear or exponential) between attempts
  • Retry only if the exception is an instance of a specific exception type.
  • Optional logging of attempts
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

Usage:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

See my post for more info.


回答 17

这是我的解决方法:

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

Here’s my idea on how to fix this:

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

回答 18

我最近与我的python合作解决了这个问题,很高兴与stackoverflow访问者分享它,如果需要的话请提供反馈。

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

i recently worked with my python on a solution to this problem and i am happy to share it with stackoverflow visitors please give feedback if it is needed.

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

回答 19

仅在try子句成功时增加循环变量

increment your loop variable only when the try clause succeeds


遇到异常时,如何获取类型,文件和行号?

问题:遇到异常时,如何获取类型,文件和行号?

捕获将这样打印的异常:

Traceback (most recent call last):
  File "c:/tmp.py", line 1, in <module>
    4 / 0
ZeroDivisionError: integer division or modulo by zero

我想将其格式化为:

ZeroDivisonError, tmp.py, 1

Catching an exception that would print like this:

Traceback (most recent call last):
  File "c:/tmp.py", line 1, in <module>
    4 / 0
ZeroDivisionError: integer division or modulo by zero

I want to format it into:

ZeroDivisonError, tmp.py, 1

回答 0

import sys, os

try:
    raise NotImplementedError("No error")
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print(exc_type, fname, exc_tb.tb_lineno)
import sys, os

try:
    raise NotImplementedError("No error")
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print(exc_type, fname, exc_tb.tb_lineno)

回答 1

对我有用的最简单的形式。

import traceback

try:
    print(4/0)
except ZeroDivisionError:
    print(traceback.format_exc())

输出量

Traceback (most recent call last):
  File "/path/to/file.py", line 51, in <module>
    print(4/0)
ZeroDivisionError: division by zero

Process finished with exit code 0

Simplest form that worked for me.

import traceback

try:
    print(4/0)
except ZeroDivisionError:
    print(traceback.format_exc())

Output

Traceback (most recent call last):
  File "/path/to/file.py", line 51, in <module>
    print(4/0)
ZeroDivisionError: division by zero

Process finished with exit code 0

回答 2

traceback.format_exception()的(Py v2.7.3)和被调用/相关的函数有很大帮助。令人尴尬的是,我总是忘记阅读原始资料。我只是徒劳地寻找类似的细节之后才这样做的。一个简单的问题,“如何为异常重新创建与Python相同的输出,并具有所有相同的详细信息?” 这将使任何人获得90 %%以上的所需东西。沮丧,我想到了这个例子。希望对别人有帮助。(它肯定帮助了我!;-)

import sys, traceback

traceback_template = '''Traceback (most recent call last):
  File "%(filename)s", line %(lineno)s, in %(name)s
%(type)s: %(message)s\n''' # Skipping the "actual line" item

# Also note: we don't walk all the way through the frame stack in this example
# see hg.python.org/cpython/file/8dffb76faacc/Lib/traceback.py#l280
# (Imagine if the 1/0, below, were replaced by a call to test() which did 1/0.)

try:
    1/0
except:
    # http://docs.python.org/2/library/sys.html#sys.exc_info
    exc_type, exc_value, exc_traceback = sys.exc_info() # most recent (if any) by default

    '''
    Reason this _can_ be bad: If an (unhandled) exception happens AFTER this,
    or if we do not delete the labels on (not much) older versions of Py, the
    reference we created can linger.

    traceback.format_exc/print_exc do this very thing, BUT note this creates a
    temp scope within the function.
    '''

    traceback_details = {
                         'filename': exc_traceback.tb_frame.f_code.co_filename,
                         'lineno'  : exc_traceback.tb_lineno,
                         'name'    : exc_traceback.tb_frame.f_code.co_name,
                         'type'    : exc_type.__name__,
                         'message' : exc_value.message, # or see traceback._some_str()
                        }

    del(exc_type, exc_value, exc_traceback) # So we don't leave our local labels/objects dangling
    # This still isn't "completely safe", though!
    # "Best (recommended) practice: replace all exc_type, exc_value, exc_traceback
    # with sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

    print
    print traceback.format_exc()
    print
    print traceback_template % traceback_details
    print

在此查询的特定答案中:

sys.exc_info()[0].__name__, os.path.basename(sys.exc_info()[2].tb_frame.f_code.co_filename), sys.exc_info()[2].tb_lineno

Source (Py v2.7.3) for traceback.format_exception() and called/related functions helps greatly. Embarrassingly, I always forget to Read the Source. I only did so for this after searching for similar details in vain. A simple question, “How to recreate the same output as Python for an exception, with all the same details?” This would get anybody 90+% to whatever they’re looking for. Frustrated, I came up with this example. I hope it helps others. (It sure helped me! ;-)

import sys, traceback

traceback_template = '''Traceback (most recent call last):
  File "%(filename)s", line %(lineno)s, in %(name)s
%(type)s: %(message)s\n''' # Skipping the "actual line" item

# Also note: we don't walk all the way through the frame stack in this example
# see hg.python.org/cpython/file/8dffb76faacc/Lib/traceback.py#l280
# (Imagine if the 1/0, below, were replaced by a call to test() which did 1/0.)

try:
    1/0
except:
    # http://docs.python.org/2/library/sys.html#sys.exc_info
    exc_type, exc_value, exc_traceback = sys.exc_info() # most recent (if any) by default

    '''
    Reason this _can_ be bad: If an (unhandled) exception happens AFTER this,
    or if we do not delete the labels on (not much) older versions of Py, the
    reference we created can linger.

    traceback.format_exc/print_exc do this very thing, BUT note this creates a
    temp scope within the function.
    '''

    traceback_details = {
                         'filename': exc_traceback.tb_frame.f_code.co_filename,
                         'lineno'  : exc_traceback.tb_lineno,
                         'name'    : exc_traceback.tb_frame.f_code.co_name,
                         'type'    : exc_type.__name__,
                         'message' : exc_value.message, # or see traceback._some_str()
                        }

    del(exc_type, exc_value, exc_traceback) # So we don't leave our local labels/objects dangling
    # This still isn't "completely safe", though!
    # "Best (recommended) practice: replace all exc_type, exc_value, exc_traceback
    # with sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

    print
    print traceback.format_exc()
    print
    print traceback_template % traceback_details
    print

In specific answer to this query:

sys.exc_info()[0].__name__, os.path.basename(sys.exc_info()[2].tb_frame.f_code.co_filename), sys.exc_info()[2].tb_lineno

回答 3

这是显示发生异常的行号的示例。

import sys
try:
    print(5/0)
except Exception as e:
    print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)

print('And the rest of program continues')

Here is an example of showing the line number of where exception takes place.

import sys
try:
    print(5/0)
except Exception as e:
    print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)

print('And the rest of program continues')

在Python中获取异常值

问题:在Python中获取异常值

如果我有该代码:

try:
    some_method()
except Exception, e:

我如何获得此Exception值(我的意思是字符串表示)?

If I have that code:

try:
    some_method()
except Exception, e:

How can I get this Exception value (string representation I mean)?


回答 0

str

try:
    some_method()
except Exception as e:
    s = str(e)

同样,大多数异常类都具有args属性。通常,args[0]将是错误消息。

应该注意的是,str如果没有错误消息,仅使用将返回一个空字符串,而使用reprpyfunc建议使用至少将显示异常的类。我的看法是,如果要打印出来,它是针对最终用户的,它并不关心类是什么,只需要一条错误消息。

它实际上取决于您要处理的异常类以及如何实例化它。您有什么特别的想法吗?

use str

try:
    some_method()
except Exception as e:
    s = str(e)

Also, most exception classes will have an args attribute. Often, args[0] will be an error message.

It should be noted that just using str will return an empty string if there’s no error message whereas using repr as pyfunc recommends will at least display the class of the exception. My take is that if you’re printing it out, it’s for an end user that doesn’t care what the class is and just wants an error message.

It really depends on the class of exception that you are dealing with and how it is instantiated. Did you have something in particular in mind?


回答 1

使用repr()和使用repr和str之间的区别

使用repr

>>> try:
...     print(x)
... except Exception as e:
...     print(repr(e))
... 
NameError("name 'x' is not defined")

使用str

>>> try:
...     print(x)
... except Exception as e:
...     print(str(e))
... 
name 'x' is not defined

Use repr() and The difference between using repr and str

Using repr:

>>> try:
...     print(x)
... except Exception as e:
...     print(repr(e))
... 
NameError("name 'x' is not defined")

Using str:

>>> try:
...     print(x)
... except Exception as e:
...     print(str(e))
... 
name 'x' is not defined

回答 2

即使我意识到这是一个老问题,我还是建议使用traceback模块来处理异常的输出。

用于traceback.print_exc()将当前异常打印为标准错误,就像在未捕获的情况下将其打印一样,或者traceback.format_exc()将其作为字符串输出。如果要限制输出,或者可以将打印重定向到类似文件的对象,则可以将各种参数传递给这些函数中的任何一个。

Even though I realise this is an old question, I’d like to suggest using the traceback module to handle output of the exceptions.

Use traceback.print_exc() to print the current exception to standard error, just like it would be printed if it remained uncaught, or traceback.format_exc() to get the same output as a string. You can pass various arguments to either of those functions if you want to limit the output, or redirect the printing to a file-like object.


回答 3

尚未给出另一种方式:

try:
    1/0
except Exception, e:
    print e.message

输出:

integer division or modulo by zero

args[0] 可能实际上不是消息。

str(e)可能返回带引号的字符串,u如果可能,则返回带前导的字符串:

'integer division or modulo by zero'

repr(e) 给出完整的异常表示,这可能不是您想要的:

"ZeroDivisionError('integer division or modulo by zero',)"

编辑

我的错 !!!似乎BaseException.message 已从弃用2.6,最后,似乎仍然没有标准化的方式来显示异常消息。所以我想最好是做处理e.argsstr(e)根据您的需要(也可能是e.message,如果你正在使用的lib是依靠这一机制)。

例如,使用pygraphvize.message是正确显示异常的唯一方法,使用str(e)将消息包围u''

但是,使用MySQLdb,检索消息的正确方法是e.args[1]e.message为空,str(e)并将显示'(ERR_CODE, "ERR_MSG")'

Another way hasn’t been given yet:

try:
    1/0
except Exception, e:
    print e.message

Output:

integer division or modulo by zero

args[0] might actually not be a message.

str(e) might return the string with surrounding quotes and possibly with the leading u if unicode:

'integer division or modulo by zero'

repr(e) gives the full exception representation which is not probably what you want:

"ZeroDivisionError('integer division or modulo by zero',)"

edit

My bad !!! It seems that BaseException.message has been deprecated from 2.6, finally, it definitely seems that there is still not a standardized way to display exception messages. So I guess the best is to do deal with e.args and str(e) depending on your needs (and possibly e.message if the lib you are using is relying on that mechanism).

For instance, with pygraphviz, e.message is the only way to display correctly the exception, using str(e) will surround the message with u''.

But with MySQLdb, the proper way to retrieve the message is e.args[1]: e.message is empty, and str(e) will display '(ERR_CODE, "ERR_MSG")'


回答 4

对于python2,最好使用e.message来获取异常消息,这样可以避免可能UnicodeDecodeError。但是yes e.message对于某些异常(如)将为空OSError,在这种情况下,我们可以exc_info=True向日志记录功能中添加a ,以免出错。
对于python3,我认为使用是安全的str(e)

For python2, It’s better to use e.message to get the exception message, this will avoid possible UnicodeDecodeError. But yes e.message will be empty for some kind of exceptions like OSError, in which case we can add a exc_info=True to our logging function to not miss the error.
For python3, I think it’s safe to use str(e).


回答 5

如果您不知道错误的类型/来源,可以尝试:

import sys
try:
    doSomethingWrongHere()
except:
    print('Error: {}'.format(sys.exc_info()[0]))

但是请注意,您会收到pep8警告:

[W] PEP 8 (E722): do not use bare except

If you don’t know the type/origin of the error, you can try:

import sys
try:
    doSomethingWrongHere()
except:
    print('Error: {}'.format(sys.exc_info()[0]))

But be aware, you’ll get pep8 warning:

[W] PEP 8 (E722): do not use bare except

回答 6

要检查错误消息并对其进行处理(使用Python 3)…

try:
    some_method()
except Exception as e:
    if {value} in e.args:
        {do something}

To inspect the error message and do something with it (with Python 3)…

try:
    some_method()
except Exception as e:
    if {value} in e.args:
        {do something}

python:我怎么知道发生了什么类型的异常?

问题:python:我怎么知道发生了什么类型的异常?

我有一个主程序调用的函数:

try:
    someFunction()
except:
    print "exception happened!"

但是在执行函数的中间会引发异常,因此它跳到了该except部分。

我如何才能确切地看到someFunction()导致异常发生的原因?

I have a function called by the main program:

try:
    someFunction()
except:
    print "exception happened!"

but in the middle of the execution of the function it raises exception, so it jumps to the except part.

How can I see exactly what happened in the someFunction() that caused the exception to happen?


回答 0

其他答案都指出,您不应捕获通用异常,但似乎没有人想告诉您原因,这对于理解何时可以打破“规则”至关重要。是一个解释。基本上是这样,所以您不会隐藏:

因此,只要您不做任何事情,就可以捕获通用异常。例如,您可以通过另一种方式向用户提供有关异常的信息,例如:

  • 在GUI中将异常显示为对话框
  • 将异常从工作线程或进程转移到多线程或多处理应用程序中的控制线程或进程

那么如何捕获通用异常呢?有几种方法。如果只需要异常对象,请按照以下步骤操作:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

确保 message被带到用户的注意力在一个难以错过的方式!如上所示,如果将消息掩埋在许多其他消息中,则可能不够用。未能引起用户的注意无异于吞没所有exceptions,如果您有任何印象,在阅读完本页上的答案后应该会消失,这不是一件好事。在except块末尾添加一个raise语句将通过透明地重新引发捕获的异常来解决该问题。

上面的代码和使用except:不带任何参数的代码之间的区别是双重的:

  • 裸机except:不会给您检查异常对象
  • 上面的代码通常不会捕获这些异常SystemExitKeyboardInterrupt并且GeneratorExit通常是您想要的。请参阅异常层次结构

如果您还希望在不捕获异常的情况下获得相同的堆栈跟踪,则可以这样获取(仍在except子句内):

import traceback
print traceback.format_exc()

如果您使用logging模块,则可以将异常打印到日志(以及消息)中,如下所示:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

如果您想更深入地研究堆栈,查看变量等,请使用except块内的模块post_mortem功能pdb

import pdb
pdb.post_mortem()

我发现在寻找错误时,这最后一种方法是无价的。

The other answers all point out that you should not catch generic exceptions, but no one seems to want to tell you why, which is essential to understanding when you can break the “rule”. Here is an explanation. Basically, it’s so that you don’t hide:

So as long as you take care to do none of those things, it’s OK to catch the generic exception. For instance, you could provide information about the exception to the user another way, like:

  • Present exceptions as dialogs in a GUI
  • Transfer exceptions from a worker thread or process to the controlling thread or process in a multithreading or multiprocessing application

So how to catch the generic exception? There are several ways. If you just want the exception object, do it like this:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Make sure message is brought to the attention of the user in a hard-to-miss way! Printing it, as shown above, may not be enough if the message is buried in lots of other messages. Failing to get the users attention is tantamount to swallowing all exceptions, and if there’s one impression you should have come away with after reading the answers on this page, it’s that this is not a good thing. Ending the except block with a raise statement will remedy the problem by transparently reraising the exception that was caught.

The difference between the above and using just except: without any argument is twofold:

  • A bare except: doesn’t give you the exception object to inspect
  • The exceptions SystemExit, KeyboardInterrupt and GeneratorExit aren’t caught by the above code, which is generally what you want. See the exception hierarchy.

If you also want the same stacktrace you get if you do not catch the exception, you can get that like this (still inside the except clause):

import traceback
print traceback.format_exc()

If you use the logging module, you can print the exception to the log (along with a message) like this:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

If you want to dig deeper and examine the stack, look at variables etc., use the post_mortem function of the pdb module inside the except block:

import pdb
pdb.post_mortem()

I’ve found this last method to be invaluable when hunting down bugs.


回答 1

获取异常对象所属的类的名称:

e.__class__.__name__

并且使用print_exc()函数还将打印堆栈跟踪,这对于任何错误消息都是必不可少的信息。

像这样:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

您将获得如下输出:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

在打印和分析之后,代码可以决定不处理异常,而只是执行raise

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

输出:

special case of CustomException not interfering

解释器输出异常:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

经过raise最初的异常继续进一步传播调用堆栈。(当心可能的陷阱)如果引发新的异常,它将产生新的(较短的)堆栈跟踪。

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

输出:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

请注意,traceback如何不包括calculate()来自9作为原始异常源的line函数e

Get the name of the class that exception object belongs:

e.__class__.__name__

and using print_exc() function will also print stack trace which is essential info for any error message.

Like this:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

You will get output like this:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

And after print and analysis, the code can decide not to handle exception and just execute raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Output:

special case of CustomException not interfering

And interpreter prints exception:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

After raise original exception continues to propagate further up the call stack. (Beware of possible pitfall) If you raise new exception it caries new (shorter) stack trace.

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Output:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Notice how traceback does not include calculate() function from line 9 which is the origin of original exception e.


回答 2

通常,您不应捕获所有可能的异常,try: ... except因为这过于广泛。只要抓住由于任何原因而可能发生的事件。如果确实需要,例如,如果您想在调试时查找有关某个问题的更多信息,则应该这样做

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

You usually should not catch all possible exceptions with try: ... except as this is overly broad. Just catch those that are expected to happen for whatever reason. If you really must, for example if you want to find out more about some problem while debugging, you should do

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

回答 3

除非somefunction是一个非常糟糕的编码遗留函数,否则您不需要所要的内容。

使用multiple except子句以不同的方式处理不同的异常:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

要点是,您不应捕获一般异常,而应捕获所需的异常。我确定您不想掩盖意外的错误或错误。

Unless somefunction is a very bad coded legacy function, you shouldn’t need what you’re asking.

Use multiple except clause to handle in different ways different exceptions:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

The main point is that you shouldn’t catch generic exception, but only the ones that you need to. I’m sure that you don’t want to shadow unexpected errors or bugs.


回答 4

大多数答案都指向except (…) as (…):语法(正确地是这样),但与此同时,没有人愿意谈论房间里的大象,那里的大象是有sys.exc_info()功能的。从文档SYS模块(重点煤矿):

此函数返回三个值的元组,它们给出有关当前正在处理的异常的信息。
(…)
如果没有在堆栈上的任何地方处理异常,则返回包含三个None值的元组。否则,返回的值是(类型,值,回溯)。它们的含义是:type获取要处理的异常的类型(BaseException的子类);value获取异常实例(异常类型的实例);traceback获取一个traceback对象(请参见参考手册),该对象将调用堆栈封装在最初发生异常的位置。

我认为sys.exc_info()可以将其视为原始问题“ 我如何知道发生了哪种异常的最直接答案”

Most answers point to except (…) as (…): syntax (rightly so) but at the same time nobody wants to talk about an elephant in the room, where the elephant is sys.exc_info() function. From the documentation of sys module (emphasis mine):

This function returns a tuple of three values that give information about the exception that is currently being handled.
(…)
If no exception is being handled anywhere on the stack, a tuple containing three None values is returned. Otherwise, the values returned are (type, value, traceback). Their meaning is: type gets the type of the exception being handled (a subclass of BaseException); value gets the exception instance (an instance of the exception type); traceback gets a traceback object (see the Reference Manual) which encapsulates the call stack at the point where the exception originally occurred.

I think the sys.exc_info() could be treated as the most direct answer to the original question of How do I know what type of exception occurred?


回答 5

尝试:someFunction()除外,exceptions:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

try: someFunction() except Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

回答 6

这些答案非常适合调试,但是对于以编程方式测试异常来说,isinstance(e, SomeException)它很方便,因为它也可以测试子类SomeException,因此您可以创建适用于异常层次结构的功能。

These answers are fine for debugging, but for programmatically testing the exception, isinstance(e, SomeException) can be handy, as it tests for subclasses of SomeException too, so you can create functionality that applies to hierarchies of exceptions.


回答 7

这是我处理异常的方式。这样做的想法是尝试解决问题,如果可能的话,然后再添加一个更理想的解决方案。不要在生成异常的代码中解决问题,否则该代码会失去对原始算法的跟踪,应将其写入现场。但是,传递解决问题所需的数据,并返回lambda,以防万一您无法在生成它的代码之外解决问题。

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

目前,由于我不想与应用程序的目的相切,所以我没有添加任何复杂的解决方案。但是将来,当我更多地了解可能的解决方案时(由于该应用程序的设计更多),我可以添加一个由索引的解决方案字典during

在所示的示例中,一种解决方案可能是查找存储在其他位置的应用程序数据,例如说是否“ app.p”文件被误删除了。

目前,由于编写异常处理程序不是一个聪明的主意(我们尚不知道解决异常的最佳方法,因为应用程序设计会不断发展),因此我们仅返回简单的修复程序,其作用就像我们在运行一样首次使用该应用(在这种情况下)。

Here’s how I’m handling my exceptions. The idea is to do try solving the issue if that’s easy, and later add a more desirable solution if possible. Don’t solve the issue in the code that generates the exception, or that code loses track of the original algorithm, which should be written to-the-point. However, pass what data is needed to solve the issue, and return a lambda just in case you can’t solve the problem outside of the code that generates it.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

For now, since I don’t want to think tangentially to my app’s purpose, I haven’t added any complicated solutions. But in the future, when I know more about possible solutions (since the app is designed more), I could add in a dictionary of solutions indexed by during.

In the example shown, one solution might be to look for app data stored somewhere else, say if the ‘app.p’ file got deleted by mistake.

For now, since writing the exception handler is not a smart idea (we don’t know the best ways to solve it yet, because the app design will evolve), we simply return the easy fix which is to act like we’re running the app for the first time (in this case).


回答 8

为了增加Lauritz的答案,我创建了一个用于处理异常的装饰器/包装器,并且包装器记录了发生哪种类型的异常。

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

这可以在类方法或带有装饰器的独立函数上调用:

@general_function_handler

请参阅我的博客,以获取完整示例:http : //ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

To add to Lauritz’s answer, I created a decorator/wrapper for exception handling and the wrapper logs which type of exception occurred.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

This can be called on a class method or a standalone function with the decorator:

@general_function_handler

See my blog about for the full example: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


回答 9

您可以按照Lauritz的建议开始:

except Exception as ex:

然后就print ex这样:

try:
    #your try code here
except Exception as ex:
    print ex

You can start as Lauritz recommended, with:

except Exception as ex:

and then just to print ex like so:

try:
    #your try code here
except Exception as ex:
    print ex

回答 10

可以通过以下方式捕获实际的异常:

try:
    i = 1/0
except Exception as e:
    print e

您可以从The Python Tutorial了解更多有关异常的信息。

The actual exception can be captured in the following way:

try:
    i = 1/0
except Exception as e:
    print e

You can learn more about exceptions from The Python Tutorial.


回答 11

您的问题是:“我如何才能确切地看到someFunction()中发生了什么导致异常发生?”

在我看来,您不是在问如何在生产代码中处理无法预料的异常(假设有很多答案),而是如何找出导致开发过程中特定异常的原因。

最简单的方法是使用调试器,该调试器可以在发生未捕获的异常的地方停止(最好不退出),以便您可以检查变量。例如,Eclipse开源IDE中的PyDev可以做到这一点。要在Eclipse中启用它,请打开Debug透视图,Manage Python Exception BreakpointsRun菜单中选择,然后选中Suspend on uncaught exceptions

Your question is: “How can I see exactly what happened in the someFunction() that caused the exception to happen?”

It seems to me that you are not asking about how to handle unforeseen exceptions in production code (as many answers assumed), but how to find out what is causing a particular exception during development.

The easiest way is to use a debugger that can stop where the uncaught exception occurs, preferably not exiting, so that you can inspect the variables. For example, PyDev in the Eclipse open source IDE can do that. To enable that in Eclipse, open the Debug perspective, select Manage Python Exception Breakpoints in the Run menu, and check Suspend on uncaught exceptions.


回答 12

仅避免捕获异常,Python打印的回溯将告诉您发生了什么异常。

Just refrain from catching the exception and the traceback that Python prints will tell you what exception occurred.


为什么返回NotImplemented而不是引发NotImplementedError

问题:为什么返回NotImplemented而不是引发NotImplementedError

Python有一个称为的单例NotImplemented

为什么有人想返回NotImplemented而不是提出NotImplementedErrorexceptions?这是否会使查找错误(例如执行无效方法的代码)变得更加困难?

Python has a singleton called NotImplemented.

Why would someone want to ever return NotImplemented instead of raising the NotImplementedError exception? Won’t it just make it harder to find bugs, such as code that executes invalid methods?


回答 0

这是因为__lt__()和相关的比较方法非常普遍地间接用于列表排序等。有时,算法会选择其他方法或选择默认的获胜者。除非捕获到异常,否则引发异常将不会发生,但是NotImplemented不会引发异常,可以将其用于进一步的测试中。

http://jcalderone.livejournal.com/32837.html

总结该链接:

NotImplemented信号,运行时,它应该让别人来满足使用,在表达a == b,如果a.__eq__(b)回报率NotImplemented,那么Python尝试b.__eq__(a)。如果b知道的足够多返回TrueFalse,则表达式可以成功。如果没有,那么运行时将退回到内置行为(这是基于身份==!=)“。

It’s because __lt__() and related comparison methods are quite commonly used indirectly in list sorts and such. Sometimes the algorithm will choose to try another way or pick a default winner. Raising an exception would break out of the sort unless caught, whereas NotImplemented doesn’t get raised and can be used in further tests.

http://jcalderone.livejournal.com/32837.html

To summarise that link:

NotImplemented signals to the runtime that it should ask someone else to satisfy the operation. In the expression a == b, if a.__eq__(b) returns NotImplemented, then Python tries b.__eq__(a). If b knows enough to return True or False, then the expression can succeed. If it doesn’t, then the runtime will fall back to the built-in behavior (which is based on identity for == and !=).”


回答 1

因为它们有不同的用例。

引用文档(Python 3.6):

未实现

应该由二进制特殊的方法被返回(例如__eq__()__lt__()__add__()__rsub__(),等等),以指示该操作不相对于实施向其他类型

异常NotImplementedError

[…]在用户定义的基类中,当抽象方法要求派生类重写该方法时,或者正在开发该类以指示仍需要添加实际实现时,应引发此异常。

有关详细信息,请参见链接。

Because they have different use cases.

Quoting the docs (Python 3.6):

NotImplemented

should be returned by the binary special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type

exception NotImplementedError

[…] In user defined base classes, abstract methods should raise this exception when they require derived classes to override the method, or while the class is being developed to indicate that the real implementation still needs to be added.

See the links for details.


回答 2

原因之一是性能。在诸如丰富的比较之类的情况下,您可能会在短时间内进行大量操作,因此设置和处理大量异常可能比直接返回NotImplemented值花费更多的时间。

One reason is performance. In a situation like rich comparisons, where you could be doing lots of operations in a short time, setting up and handling lots of exceptions could take a lot longer than simply returning a NotImplemented value.


为什么列表没有像字典一样安全的“获取”方法?

问题:为什么列表没有像字典一样安全的“获取”方法?

为什么列表没有像字典一样安全的“获取”方法?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range

Why doesn’t list have a safe “get” method like dictionary?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range

回答 0

最终,它可能没有一个安全的.get方法,因为a dict是一个关联集合(值与名称相关联),在这种情况下,检查键是否存在(并返回其值)而不抛出异常是非常低效的,而这是非常琐碎的避免异常访问列表元素(因为该len方法非常快)。该.get方法允许您查询与名称关联的值,而不直接访问字典中的第37个项目(这更像是您要查询的列表中的内容)。

当然,您可以自己轻松实现此目的:

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

您甚至可以在中将其Monkey修补到__builtins__.list构造函数上__main__,但是由于大多数代码不使用它,因此更改的普及程度较小。如果您只想将此代码与自己的代码创建的列表一起使用,则可以简单地子类化list并添加get方法。

Ultimately it probably doesn’t have a safe .get method because a dict is an associative collection (values are associated with names) where it is inefficient to check if a key is present (and return its value) without throwing an exception, while it is super trivial to avoid exceptions accessing list elements (as the len method is very fast). The .get method allows you to query the value associated with a name, not directly access the 37th item in the dictionary (which would be more like what you’re asking of your list).

Of course, you can easily implement this yourself:

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

You could even monkeypatch it onto the __builtins__.list constructor in __main__, but that would be a less pervasive change since most code doesn’t use it. If you just wanted to use this with lists created by your own code you could simply subclass list and add the get method.


回答 1

如果您想要第一个元素,例如 my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

我知道这并非您真正要求的,但可能会帮助其他人。

This works if you want the first element, like my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

I know it’s not exactly what you asked for but it might help others.


回答 2

可能是因为它对列表语义没有多大意义。但是,您可以通过子类化轻松创建自己的类。

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()

Probably because it just didn’t make much sense for list semantics. However, you can easily create your own by subclassing.

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()

回答 3

代替使用.get,这样使用列表应该可以。只是用法上的差异。

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

Instead of using .get, using like this should be ok for lists. Just a usage difference.

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

回答 4

试试这个:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

Try this:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

回答 5

jose.angel.jimenez


对于“单线”粉丝…


如果需要列表的第一个元素,或者如果列表为空,则需要默认值,请尝试:

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

退货 a

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

退货 default


其他元素的示例…

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']

默认回退…

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c

经过测试 Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)

Credits to jose.angel.jimenez and Gus Bus.


For the “oneliner” fans…


If you want the first element of a list or if you want a default value if the list is empty try:

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

returns a

and

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

returns default


Examples for other elements…

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']
print(liste[3:4])  # returns []

With default fallback…

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c
print((liste[3:4] or ('default',))[0])  # returns default

Possibly shorter:

liste = ['a', 'b', 'c']
value, = liste[:1] or ('default',)
print(value)  # returns a

It looks like you need the comma before the equal sign, the equal sign and the latter parenthesis.


More general:

liste = ['a', 'b', 'c']
f = lambda l, x, d: l[x:x+1] and l[x] or d
print(f(liste, 0, 'default'))  # returns a
print(f(liste, 1, 'default'))  # returns b
print(f(liste, 2, 'default'))  # returns c
print(f(liste, 3, 'default'))  # returns default

Tested with Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)


回答 6

最好的办法是将列表转换成字典,然后使用get方法访问它:

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')

The best thing you can do is to convert the list into a dict and then access it with the get method:

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')

回答 7

因此,我对此进行了更多研究,结果发现没有任何针对此的东西。当我找到list.index(value)时,我感到很兴奋,它返回指定项目的索引,但是没有什么可用于获取特定索引处的值的。因此,如果您不想使用safe_list_get解决方案,我认为这是相当不错的。以下是一些liner if语句,这些语句可以根据情况为您完成工作:

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

您也可以使用None代替’No’,这更有意义。

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

另外,如果您只想获取列表中的第一项或最后一项,则可以使用

end_el = x[-1] if x else None

您也可以将它们变成函数,但我仍然喜欢IndexError异常解决方案。我尝试了该safe_list_get解决方案的简化版本,并使其变得更简单(没有默认设置):

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

还没有进行基准测试以了解最快的方法。

So I did some more research into this and it turns out there isn’t anything specific for this. I got excited when I found list.index(value), it returns the index of a specified item, but there isn’t anything for getting the value at a specific index. So if you don’t want to use the safe_list_get solution which I think is pretty good. Here are some 1 liner if statements that can get the job done for you depending on the scenario:

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

You can also use None instead of ‘No’, which makes more sense.:

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

Also if you want to just get the first or last item in the list, this works

end_el = x[-1] if x else None

You can also make these into functions but I still liked the IndexError exception solution. I experimented with a dummied down version of the safe_list_get solution and made it a bit simpler (no default):

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

Haven’t benchmarked to see what is fastest.


回答 8

字典用于查找。询问条目是否存在是很有意义的。列表通常是迭代的。通常不问L [10]是否存在,而是问L的长度是否为11。

Dictionaries are for look ups. It makes sense to ask if an entry exists or not. Lists are usually iterated. It isn’t common to ask if L[10] exists but rather if the length of L is 11.


回答 9

您的用例基本上只与固定长度的数组和矩阵有关,这样您就可以知道它们有多长时间。在这种情况下,通常还需要在手动将它们填充为None或0之前创建它们,以便实际上您将使用的任何索引都已经存在。

你可以这样说:我经常在字典上需要.get()。作为一名全职程序员十年后,我认为我从不需要它了。:)

Your usecase is basically only relevant for when doing arrays and matrixes of a fixed length, so that you know how long they are before hand. In that case you typically also create them before hand filling them up with None or 0, so that in fact any index you will use already exists.

You could say this: I need .get() on dictionaries quite often. After ten years as a full time programmer I don’t think I have ever needed it on a list. :)


为什么“ except:pass”是不好的编程习惯?

问题:为什么“ except:pass”是不好的编程习惯?

我经常看到有关except: pass不鼓励使用的其他Stack Overflow问题的评论。为什么这样不好?有时我只是不在乎错误是什么,我只想继续编写代码。

try:
    something
except:
    pass

为什么使用except: pass积木不好?是什么让它不好?是我pass出错还是我except出错了?

I often see comments on other Stack Overflow questions about how the use of except: pass is discouraged. Why is this bad? Sometimes I just don’t care what the errors, are and I want to just continue with the code.

try:
    something
except:
    pass

Why is using an except: pass block bad? What makes it bad? Is it the fact that I pass on an error or that I except any error?


回答 0

正如您正确猜测的那样,它有两个方面:通过在之后不指定任何异常类型来捕获任何错误except,并在不采取任何操作的情况下简单地传递它。

我的解释是“更长”的时间,所以tl; dr可以细分为:

  1. 不要发现任何错误。始终指定您准备从中恢复的异常,并且仅捕获这些异常。
  2. 尽量避免传入除了blocks。除非明确要求,否则通常不是一个好兆头。

但是,让我们详细介绍一下:

不要发现任何错误

使用try块时,通常这样做是因为您知道有可能引发异常。这样,您还已经大概知道了哪些会中断,哪些异常会引发。在这种情况下,您会捕获异常,因为您可以从中积极地恢复过来。这意味着您已为exceptions做好了准备,并有一些替代计划,在发生这种exceptions时将遵循该计划。

例如,当您要求用户输入数字时,您可以使用int()可能引起的转换输入ValueError。您可以简单地要求用户再试一次,从而轻松地恢复它,因此捕获ValueError并再次提示用户将是一个适当的计划。一个不同的例子是,如果您想从文件中读取某些配置,而该文件恰好不存在。因为它是一个配置文件,所以您可能具有一些默认配置作为后备,因此该文件并非完全必要。因此,FileNotFoundError在此处捕获并简单地应用默认配置将是一个不错的计划。现在,在这两种情况下,我们都期望有一个非常具体的exceptions,并且有一个同样具体的计划可以从中恢复。因此,在每种情况下,我们只明确except 某些 exceptions。

但是,如果我们要抓住一切,那么除了准备好从那些异常中恢复过来,我们还有机会获得我们没有想到的异常,而我们确实无法从中恢复。或不应从中恢复。

让我们以上面的配置文件示例为例。如果文件丢失,我们将应用默认配置,并可能在以后决定自动保存配置(因此下次该文件存在)。现在想象我们得到一个IsADirectoryError或一个PermissionError代替。在这种情况下,我们可能不想继续。我们仍然可以应用默认配置,但是以后将无法保存文件。而且用户可能也打算具有自定义配置,因此可能不需要使用默认值。因此,我们希望立即将其告知用户,并且可能也中止程序执行。但这不是我们想要在某些小代码部分的深处做的事情。这在应用程序级别上很重要,因此应该在顶部进行处理-因此让异常冒出来。

Python 2习惯用法文档中还提到了另一个简单的示例。在这里,代码中存在一个简单的错字,导致它中断。因为我们正在捕获每个异常,所以我们也捕获了NameErrorsSyntaxErrors。两者都是编程时我们所有人都会遇到的错误。两者都是我们在交付代码时绝对不希望包含的错误。但是,因为我们也抓住了它们,所以我们甚至都不知道它们在那里发生,并且失去了正确调试它的任何帮助。

但是,还有一些危险的exceptions情况,我们不太可能为此做好准备。例如,SystemError通常很少发生,我们无法真正计划。这意味着发生了一些更复杂的事情,有可能阻止我们继续当前的任务。

无论如何,您几乎不可能为代码中的一小部分做好一切准备,因此,实际上,您应该只捕获准备好的那些异常。有人建议至少要赶上Exception它,因为它不会包含类似的内容,SystemExitKeyboardInterrupt这些内容在设计上是要终止您的应用程序的,但是我认为这仍然过于不确定。我个人只在一个地方接受捕捞活动,Exception或者在任何地方异常,并且在单个全局应用程序级异常处理程序中,该异常处理程序的唯一目的是记录我们没有准备好的任何异常。这样,我们仍然可以保留有关意外异常的尽可能多的信息,然后我们可以使用这些信息来扩展代码以显式处理这些异常(如果可以从异常中恢复),或者在发生错误的情况下创建测试用例以确保它不会再发生。但是,当然,只有当我们只捕获到我们已经期望的异常时,这才起作用,所以我们没有想到的异常自然会冒出来。

尽量避免传入除了块

当显式地捕获少量特定异常时,在许多情况下,只要不执行任何操作就可以了。在这种情况下,拥有except SomeSpecificException: pass就好。不过,在大多数情况下,情况并非如此,因为我们可能需要一些与恢复过程相关的代码(如上所述)。例如,这可以是重试该操作的内容,也可以是设置默认值的内容。

但是,如果不是这种情况,例如因为我们的代码已经被构造为可以重复执行直到成功,那么传递就足够了。从上面的例子中,我们可能想要求用户输入一个数字。因为我们知道用户不想按照我们的要求去做,所以我们可能首先将其放入循环中,因此看起来可能像这样:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

因为我们一直努力直到没有异常抛出,所以我们不需要在except块中做任何特殊的事情,所以这很好。但是,当然,有人可能会认为我们至少要向用户显示一些错误消息,以告诉他为什么他必须重复输入。

但是,在许多其他情况下,仅传递except一个信号就表明我们并未真正为所捕获的异常做好准备。除非这些异常很简单(如ValueErrorTypeError),并且我们可以通过的原因很明显,否则请尝试避免仅通过。如果真的无事可做(您对此绝对有把握),则考虑添加评论,为什么会这样;否则,展开except块以实际包括一些恢复代码。

except: pass

不过,最严重的罪犯是两者的结合。这意味着我们乐于捕捉任何错误,尽管我们绝对没有为此做好准备,并且我们也不对此做任何事情。您至少要记录该错误,还可能重新引发该错误以仍然终止应用程序(在出现MemoryError后,您不太可能像往常一样继续操作)。只是传递信息不仅可以使应用程序保持一定的生命力(当然,还取决于您捕获的位置),而且还会丢弃所有信息,从而无法发现错误-如果您不是发现错误的人,则尤其如此。


因此,底线是:仅捕获您真正期望并准备从中恢复的异常;其他所有问题都可能是您应纠正的错误,或者您没有准备好应对。如果您真的不需要对异常进行处理,则传递特定的异常很好。在其他所有情况下,这只是推定和懒惰的标志。您肯定想解决该问题。

As you correctly guessed, there are two sides to it: Catching any error by specifying no exception type after except, and simply passing it without taking any action.

My explanation is “a bit” longer—so tl;dr it breaks down to this:

  1. Don’t catch any error. Always specify which exceptions you are prepared to recover from and only catch those.
  2. Try to avoid passing in except blocks. Unless explicitly desired, this is usually not a good sign.

But let’s go into detail:

Don’t catch any error

When using a try block, you usually do this because you know that there is a chance of an exception being thrown. As such, you also already have an approximate idea of what can break and what exception can be thrown. In such cases, you catch an exception because you can positively recover from it. That means that you are prepared for the exception and have some alternative plan which you will follow in case of that exception.

For example, when you ask for the user to input a number, you can convert the input using int() which might raise a ValueError. You can easily recover that by simply asking the user to try it again, so catching the ValueError and prompting the user again would be an appropriate plan. A different example would be if you want to read some configuration from a file, and that file happens to not exist. Because it is a configuration file, you might have some default configuration as a fallback, so the file is not exactly necessary. So catching a FileNotFoundError and simply applying the default configuration would be a good plan here. Now in both these cases, we have a very specific exception we expect and have an equally specific plan to recover from it. As such, in each case, we explicitly only except that certain exception.

However, if we were to catch everything, then—in addition to those exceptions we are prepared to recover from—there is also a chance that we get exceptions that we didn’t expect, and which we indeed cannot recover from; or shouldn’t recover from.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration, and might decided at a later point to automatically save the configuration (so next time, the file exists). Now imagine we get a IsADirectoryError, or a PermissionError instead. In such cases, we probably do not want to continue; we could still apply our default configuration, but we later won’t be able to save the file. And it’s likely that the user meant to have a custom configuration too, so using the default values is likely not desired. So we would want to tell the user about it immediately, and probably abort the program execution too. But that’s not something we want to do somewhere deep within some small code part; this is something of application-level importance, so it should be handled at the top—so let the exception bubble up.

Another simple example is also mentioned in the Python 2 idioms document. Here, a simple typo exists in the code which causes it to break. Because we are catching every exception, we also catch NameErrors and SyntaxErrors. Both are mistakes that happen to us all while programming; and both are mistakes we absolutely don’t want to include when shipping the code. But because we also caught those, we won’t even know that they occurred there and lose any help to debug it correctly.

But there are also more dangerous exceptions which we are unlikely prepared for. For example SystemError is usually something that happens rarely and which we cannot really plan for; it means there is something more complicated going on, something that likely prevents us from continuing the current task.

In any case, it’s very unlikely that you are prepared for everything in a small scale part of the code, so that’s really where you should only catch those exceptions you are prepared for. Some people suggest to at least catch Exception as it won’t include things like SystemExit and KeyboardInterrupt which by design are to terminate your application, but I would argue that this is still far too unspecific. There is only one place where I personally accept catching Exception or just any exception, and that is in a single global application-level exception handler which has the single purpose to log any exception we were not prepared for. That way, we can still retain as much information about unexpected exceptions, which we then can use to extend our code to handle those explicitly (if we can recover from them) or—in case of a bug—to create test cases to make sure it won’t happen again. But of course, that only works if we only ever caught those exceptions we were already expecting, so the ones we didn’t expect will naturally bubble up.

Try to avoid passing in except blocks

When explicitly catching a small selection of specific exceptions, there are many situations in which we will be fine by simply doing nothing. In such cases, just having except SomeSpecificException: pass is just fine. Most of the time though, this is not the case as we likely need some code related to the recovery process (as mentioned above). This can be for example something that retries the action again, or to set up a default value instead.

If that’s not the case though, for example because our code is already structured to repeat until it succeeds, then just passing is good enough. Taking our example from above, we might want to ask the user to enter a number. Because we know that users like to not do what we ask them for, we might just put it into a loop in the first place, so it could look like this:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

Because we keep trying until no exception is thrown, we don’t need to do anything special in the except block, so this is fine. But of course, one might argue that we at least want to show the user some error message to tell him why he has to repeat the input.

In many other cases though, just passing in an except is a sign that we weren’t really prepared for the exception we are catching. Unless those exceptions are simple (like ValueError or TypeError), and the reason why we can pass is obvious, try to avoid just passing. If there’s really nothing to do (and you are absolutely sure about it), then consider adding a comment why that’s the case; otherwise, expand the except block to actually include some recovery code.

except: pass

The worst offender though is the combination of both. This means that we are willingly catching any error although we are absolutely not prepared for it and we also don’t do anything about it. You at least want to log the error and also likely reraise it to still terminate the application (it’s unlikely you can continue like normal after a MemoryError). Just passing though will not only keep the application somewhat alive (depending where you catch of course), but also throw away all the information, making it impossible to discover the error—which is especially true if you are not the one discovering it.


So the bottom line is: Catch only exceptions you really expect and are prepared to recover from; all others are likely either mistakes you should fix, or something you are not prepared for anyway. Passing specific exceptions is fine if you really don’t need to do something about them. In all other cases, it’s just a sign of presumption and being lazy. And you definitely want to fix that.


回答 1

这里的主要问题是它会忽略所有错误:内存不足,CPU正在燃烧,用户想要停止,程序想要退出,Jabberwocky正在杀死用户。

这太多了。在您的脑海中,您正在思考“我想忽略此网络错误”。如果出乎意料的地方出了问题,那么您的代码将以无人能及的方式以无法预测的方式静默继续并中断。

这就是为什么您应该将自己限制为仅忽略某些错误,而让其余错误通过。

The main problem here is that it ignores all and any error: Out of memory, CPU is burning, user wants to stop, program wants to exit, Jabberwocky is killing users.

This is way too much. In your head, you’re thinking “I want to ignore this network error”. If something unexpected goes wrong, then your code silently continues and breaks in completely unpredictable ways that no one can debug.

That’s why you should limit yourself to ignoring specifically only some errors and let the rest pass.


回答 2

从字面上执行伪代码甚至不会给出任何错误:

try:
    something
except:
    pass

就像是一段完全有效的代码,而不是抛出NameError。我希望这不是您想要的。

Executing your pseudo code literally does not even give any error:

try:
    something
except:
    pass

as if it is a perfectly valid piece of code, instead of throwing a NameError. I hope this is not what you want.


回答 3

为什么“ except:pass”是不好的编程习惯?

为什么这样不好?

try:
    something
except:
    pass

这会捕获所有可能的异常,包括GeneratorExitKeyboardInterruptSystemExit-这是您可能不打算捕获的异常。和赶上一样BaseException

try:
    something
except BaseException:
    pass

版本的文档说

由于Python中的每个错误都会引发一个异常,因此使用except:可能会使许多编程错误看起来像运行时问题,从而阻碍了调试过程。

Python异常层次结构

如果捕获父异常类,那么还将捕获其所有子类。仅捕获您准备处理的异常要优雅得多。

这是Python 3 异常层次结构 -您是否真的想抓住一切?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

不要这样

如果您使用这种形式的异常处理:

try:
    something
except: # don't just do a bare except!
    pass

这样,您将无法something使用Ctrl-C 中断您的代码块。您的程序将忽略try代码块内的所有可能的Exception 。

这是另一个具有相同不良行为的示例:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

相反,请尝试仅捕获您要查找的特定异常。例如,如果您知道转换可能会产生价值错误:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

另一个示例的进一步说明

您之所以这样做,是因为您一直在爬网并说a UnicodeError,但是由于使用了最广泛的Exception catch,您的代码(可能有其他基本缺陷)将尝试运行至完成,浪费带宽,处理时间,设备的磨损,内存不足,收集垃圾数据等。

如果其他人要求您完成操作,以便他们可以依靠您的代码,那么我理解被迫仅处理所有事情。但是,如果您愿意在开发过程中大声失败,那么您将有机会纠正可能会间歇性出现的问题,但这将是长期的代价高昂的错误。

通过更精确的错误处理,您的代码可以更强大。

Why is “except: pass” a bad programming practice?

Why is this bad?

try:
    something
except:
    pass

This catches every possible exception, including GeneratorExit, KeyboardInterrupt, and SystemExit – which are exceptions you probably don’t intend to catch. It’s the same as catching BaseException.

try:
    something
except BaseException:
    pass

Older versions of the documentation say:

Since every error in Python raises an exception, using except: can make many programming errors look like runtime problems, which hinders the debugging process.

Python Exception Hierarchy

If you catch a parent exception class, you also catch all of their child classes. It is much more elegant to only catch the exceptions you are prepared to handle.

Here’s the Python 3 exception hierarchy – do you really want to catch ’em all?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Don’t Do this

If you’re using this form of exception handling:

try:
    something
except: # don't just do a bare except!
    pass

Then you won’t be able to interrupt your something block with Ctrl-C. Your program will overlook every possible Exception inside the try code block.

Here’s another example that will have the same undesirable behavior:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

Instead, try to only catch the specific exception you know you’re looking for. For example, if you know you might get a value-error on a conversion:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

Further Explanation with another example

You might be doing it because you’ve been web-scraping and been getting say, a UnicodeError, but because you’ve used the broadest Exception catching, your code, which may have other fundamental flaws, will attempt to run to completion, wasting bandwidth, processing time, wear and tear on your equipment, running out of memory, collecting garbage data, etc.

If other people are asking you to complete so that they can rely on your code, I understand feeling compelled to just handle everything. But if you’re willing to fail noisily as you develop, you will have the opportunity to correct problems that might only pop up intermittently, but that would be long term costly bugs.

With more precise error handling, you code can be more robust.


回答 4

>>> import this

提姆·彼得斯(Tim Peters)撰写的《 Python之禅》

美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然从来没有比这更好正确的现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!

所以,这是我的看法。每当发现错误时,都应该采取措施进行处理,即将其写入日志文件或其他内容。至少,它通知您以前曾经有错误。

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

So, here is my opinion. Whenever you find an error, you should do something to handle it, i.e. write it in logfile or something else. At least, it informs you that there used to be a error.


回答 5

您至少except Exception:应避免捕获诸如SystemExit或的系统异常KeyboardInterrupt。这里是文档链接

通常,应明确定义要捕获的异常,以避免捕获不需要的异常。您应该知道忽略了哪些异常。

You should use at least except Exception: to avoid catching system exceptions like SystemExit or KeyboardInterrupt. Here’s link to docs.

In general you should define explicitly exceptions you want to catch, to avoid catching unwanted exceptions. You should know what exceptions you ignore.


回答 6

首先,它违反了Python Zen的两个原则:

  • 显式胜于隐式
  • 错误绝不能默默传递

这意味着您故意使错误静默地通过。而且,您不知道确切发生了哪个错误,因为except: pass它将捕获任何异常。

其次,如果我们试图从Python的Zen中抽象出来,并以理智的眼光说话,您应该知道,使用except:pass会使您在系统中没有知识和控制力。经验法则是在发生错误时引发异常,并采取适当的措施。如果您事先不知道该怎么做,请至少将错误记录在某个地方(并最好重新引发该异常):

try:
    something
except:
    logger.exception('Something happened')

但是,通常,如果您尝试捕获任何异常,则可能是在做错什么!

First, it violates two principles of Zen of Python:

  • Explicit is better than implicit
  • Errors should never pass silently

What it means, is that you intentionally make your error pass silently. Moreover, you don’t event know, which error exactly occurred, because except: pass will catch any exception.

Second, if we try to abstract away from the Zen of Python, and speak in term of just sanity, you should know, that using except:pass leaves you with no knowledge and control in your system. The rule of thumb is to raise an exception, if error happens, and take appropriate actions. If you don’t know in advance, what actions these should be, at least log the error somewhere (and better re-raise the exception):

try:
    something
except:
    logger.exception('Something happened')

But, usually, if you try to catch any exception, you are probably doing something wrong!


回答 7

except:pass构造实质上使在代码中涵盖的代码出现时出现的所有异常情况保持沉默。try:在运行块中。

造成这种不良习惯的原因在于,通常这并不是您真正想要的。更常见的情况是,您想沉默一些特定的情况,并且except:pass这实在是一种过时的工具。它可以完成工作,但也可以掩盖您可能未曾预料到的其他错误情况,但很可能希望以其他方式处理。

在Python中这一点尤其重要的是,通过这种语言的习惯用法,异常不一定是error。当然,就像大多数语言一样,通常也以这种方式使用它们。但是特别是Python偶尔会使用它们来实现一些代码任务的替代退出路径,这实际上并不是正常运行情况的一部分,但仍然不时出现,并且在大多数情况下甚至可以预期。SystemExit已经作为一个旧示例被提及,但是如今最常见的示例可能是StopIteration。这种使用异常的方式引起了很多争议,尤其是在迭代器和生成器首次引入Python时,但最终这个想法盛行。

The except:pass construct essentially silences any and all exceptional conditions that come up while the code covered in the try: block is being run.

What makes this bad practice is that it usually isn’t what you really want. More often, some specific condition is coming up that you want to silence, and except:pass is too much of a blunt instrument. It will get the job done, but it will also mask other error conditions that you likely haven’t anticipated, but may very well want to deal with in some other way.

What makes this particularly important in Python is that by the idioms of this language, exceptions are not necessarily errors. They’re often used this way, of course, just as in most languages. But Python in particular has occasionally used them to implement an alternative exit path from some code tasks which isn’t really part of the normal running case, but is still known to come up from time to time and may even be expected in most cases. SystemExit has already been mentioned as an old example, but the most common example nowadays may be StopIteration. Using exceptions this way caused a lot of controversy, especially when iterators and generators were first introduced to Python, but eventually the idea prevailed.


回答 8

已经说明了#1原因-它隐藏了您没有想到的错误。

(#2)- 它使您的代码难以被他人阅读和理解。如果在尝试读取文件时捕获到FileNotFoundException,那么对于另一个开发人员而言,“ catch”块应具有的功能非常明显。如果未指定异常,则需要附加注释以说明该块应执行的操作。

(#3)- 演示了惰性编程。如果使用通用的try / catch,则表明您不了解程序中可能出现的运行时错误,或者您不知道Python中可能出现的异常。捕获特定错误表明您既了解程序又了解Python引发的错误范围。这更有可能使其他开发人员和代码审阅者信任您的工作。

The #1 reason has already been stated – it hides errors that you did not expect.

(#2) – It makes your code difficult for others to read and understand. If you catch a FileNotFoundException when you are trying to read a file, then it is pretty obvious to another developer what functionality the ‘catch’ block should have. If you do not specify an exception, then you need additional commenting to explain what the block should do.

(#3) – It demonstrates lazy programming. If you use the generic try/catch, it indicates either that you do not understand the possible run-time errors in your program, or that you do not know what exceptions are possible in Python. Catching a specific error shows that you understand both your program and the range of errors that Python throws. This is more likely to make other developers and code-reviewers trust your work.


回答 9

那么,此代码产生什么输出?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

现在,假设tryexcept块是对复杂对象层次结构的数百行调用,它本身在大型程序的调用树中间被调用。当程序出错时,您从哪里开始寻找?

So, what output does this code produce?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

Now imagine the tryexcept block is hundreds of lines of calls to a complex object hierarchy, and is itself called in the middle of large program’s call tree. When the program goes wrong, where do you start looking?


回答 10

通常,您可以将任何错误/异常分为以下三种类别之一

  • 致命的:不是您的错,您无法阻止它们,也无法从中恢复。您当然不应该忽略它们并继续运行,并使程序保持未知状态。只要让错误终止您的程序,您就无能为力了。

  • 骨头:您自己的错误,很可能是由于疏忽,错误或编程错误所致。您应该修复该错误。同样,您当然应该不忽略并继续。

  • 外生的:在特殊情况下(例如找不到文件连接终止),您可能会遇到这些错误。您应该明确地处理这些错误,并且仅处理这些错误。

在任何情况下,except: pass都只会使程序处于未知状态,在这种状态下可能会造成更大的破坏。

In general, you can classify any error/exception in one of three categories:

  • Fatal: Not your fault, you cannot prevent them, you cannot recover from them. You should certainly not ignore them and continue, and leave your program in an unknown state. Just let the error terminate your program, there is nothing you can do.

  • Boneheaded: Your own fault, most likely due to an oversight, bug or programming error. You should fix the bug. Again, you should most certainly not ignore and continue.

  • Exogenous: You can expect these errors in exceptional situations, such as file not found or connection terminated. You should explicitly handle these errors, and only these.

In all cases except: pass will only leave your program in an unknown state, where it can cause more damage.


回答 11

简而言之,如果引发异常或错误,则说明存在问题。可能不是很不对劲,但是仅仅为了使用goto语句而创建,抛出和捕获错误和异常并不是一个好主意,而且很少这样做。99%的时间,某处出现问题。

需要解决的问题。就像生活中的情况一样,在编程中,如果您只是将问题搁置一旁并尝试忽略它们,那么它们就不会多次自行消失。相反,它们变得更大并成倍增加。为防止问题在您身上蔓延并进一步打击您,您可以1)消除它,然后清理残局,或者2)遏制它,然后清理残局。

只是忽略异常和错误并让它们像那样,是体验内存泄漏,出色的数据库连接,不必要的文件权限锁定等的好方法。

在极少数情况下,问题是如此的微小,琐碎,并且-除了需要try … catch块之外- 自包含的,以至于事后确实没有任何需要清理的地方。在这些情况下,这些最佳做法不一定适用。以我的经验,这通常意味着代码所做的任何事情基本上都是小巧的和可忽略的,而重试尝试或特殊消息之类的东西既不值得其复杂性也不值得其坚持下去。

在我公司,规则是几乎总是在某个陷阱中做某事,如果您什么都不做,那么您必须始终以非常充分的理由发表评论。要做任何事情时,您绝不能通过或留空捕获块。

Simply put, if an exception or error is thrown, something’s wrong. It may not be something very wrong, but creating, throwing, and catching errors and exceptions just for the sake of using goto statements is not a good idea, and it’s rarely done. 99% of the time, there was a problem somewhere.

Problems need to be dealt with. Just like how it is in life, in programming, if you just leave problems alone and try to ignore them, they don’t just go away on their own a lot of times; instead they get bigger and multiply. To prevent a problem from growing on you and striking again further down the road, you either 1) eliminate it and clean up the mess afterwards, or 2) contain it and clean up the mess afterwards.

Just ignoring exceptions and errors and leaving them be like that is a good way to experience memory leaks, outstanding database connections, needless locks on file permissions, etc.

On rare occasions, the problem is so miniscule, trivial, and – aside from needing a try…catch block – self-contained, that there really is just no mess to be cleaned up afterwards. These are the only occasions when this best practice doesn’t necessarily apply. In my experience, this has generally meant that whatever the code is doing is basically petty and forgoable, and something like retry attempts or special messages are worth neither the complexity nor holding the thread up on.

At my company, the rule is to almost always do something in a catch block, and if you don’t do anything, then you must always place a comment with a very good reason why not. You must never pass or leave an empty catch block when there is anything to be done.


回答 12

在我看来,错误是有原因出现的,我的声音很愚蠢,但这就是事实。良好的编程仅在必须处理错误时才会引发错误。另外,正如我前段时间所读到的,“ pass-Statement是一个显示代码的语句将在以后插入”,因此,如果您想拥有一个空的except-statement,可以随意这样做,但是对于一个好的程序,成为一部分。因为你不处理你应该拥有的东西。出现的异常使您有机会更正输入数据或更改数据结构,因此这些异常不会再次发生(但是在大多数情况下(网络异常,常规输入异常),异常表明程序的下一部分将无法正常执行。例如,NetworkException可能指示网络连接断开,并且该程序无法在接下来的程序步骤中发送/接收数据。

但是仅对一个执行块使用pass块是有效的,因为您仍然区分异常类型,因此,如果将所有异常块放在一个中,则它不是空的:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

可以这样重写:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

因此,即使是多个带有通过语句的except-block也会导致代码,其代码可处理特殊类型的异常。

In my opinion errors have a reason to appear, that my sound stupid, but thats the way it is. Good programming only raises errors when you have to handle them. Also, as i read some time ago, “the pass-Statement is a Statement that Shows code will be inserted later”, so if you want to have an empty except-statement feel free to do so, but for a good program there will be a part missing. because you dont handle the things you should have. Appearing exceptions give you the chance to correct input data or to change your data structure so these exceptions dont occur again (but in most cases (Network-exceptions, General input-exceptions) exceptions indicate that the next parts of the program wont execute well. For example a NetworkException can indicate a broken network-connection and the program cant send/recieve data in the next program steps.

But using a pass block for only one execption-block is valid, because you still differenciate beetween the types of exceptions, so if you put all exception-blocks in one, it is not empty:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

can be rewritten that way:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

So even multiple except-blocks with pass-statements can result in code, whose structure handles special types of exceptions.


回答 13

到目前为止提出的所有评论均有效。在可能的情况下,您需要指定要忽略的异常。在可能的情况下,您需要分析导致异常的原因,只忽略您要忽略的内容,而不要忽略其余的内容。如果异常导致应用程序“严重崩溃”,那么就这样吧,因为比起掩盖曾经发生过的问题,了解意外事件在发生时更为重要。

综上所述,不要以任何编程实践为重。真傻 总是有时间和地点进行忽略所有exceptions的阻止。

愚蠢至高无上的另一个例子是goto运算符的用法。当我在学校的时候,我们的教授教我们goto操作员,只是提起您永远不要使用它。不要相信有人告诉您xyz永远不要使用,并且在任何情况下都不会有用。总有。

All comments brought up so far are valid. Where possible you need to specify what exactly exception you want to ignore. Where possible you need to analyze what caused exception, and only ignore what you meant to ignore, and not the rest. If exception causes application to “crash spectacularly”, then be it, because it’s much more important to know the unexpected happened when it happened, than concealing that the problem ever occurred.

With all that said, do not take any programming practice as a paramount. This is stupid. There always is the time and place to do ignore-all-exceptions block.

Another example of idiotic paramount is usage of goto operator. When I was in school, our professor taught us goto operator just to mention that thou shalt not use it, EVER. Don’t believe people telling you that xyz should never be used and there cannot be a scenario when it is useful. There always is.


回答 14

错误处理在编程中非常重要。您确实需要向用户显示出了什么问题。在极少数情况下,您可以忽略这些错误。这是非常糟糕的编程习惯。

​Handling errors is very important in programming. You do need to show the user what went wrong. In very few cases you can ignore the errors. This is it is very bad programming practice.


回答 15

由于尚未提及,因此使用更好的样式contextlib.suppress

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

请注意,在提供的示例中, ,无论是否发生异常程序状态均保持不变。也就是说,somefile.tmp总是不存在。

Since it hasn’t been mentioned yet, it’s better style to use contextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

Notice that in the example provided, the program state remains the same, whether or not the exception occurs. That is to say, somefile.tmp always becomes non-existent.


我应该针对Python中的错误/非法参数组合引发哪个异常?

问题:我应该针对Python中的错误/非法参数组合引发哪个异常?

我想知道在Python中指示无效参数组合的最佳做法。我遇到过几种情况,其中您具有如下功能:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

唯一令人烦恼的是,每个包装都有自己的包装,通常略有不同BadValueError。我知道在Java中存在java.lang.IllegalArgumentException-是否众所周知每个人都将BadValueError在Python中创建自己的s还是存在另一种首选方法?

I was wondering about the best practices for indicating invalid argument combinations in Python. I’ve come across a few situations where you have a function like so:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

The only annoyance with this is that every package has its own, usually slightly differing BadValueError. I know that in Java there exists java.lang.IllegalArgumentException — is it well understood that everybody will be creating their own BadValueErrors in Python or is there another, preferred method?


回答 0

我只会提出ValueError,除非您需要更具体的exceptions。

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

这样做真的没有意义class BadValueError(ValueError):pass-您的自定义类的用法与ValueError相同,那么为什么不使用它呢?

I would just raise ValueError, unless you need a more specific exception..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

There’s really no point in doing class BadValueError(ValueError):pass – your custom class is identical in use to ValueError, so why not use that?


回答 1

我会继承 ValueError

class IllegalArgumentError(ValueError):
    pass

有时最好创建自己的异常,但要从内置异常中继承,该异常应尽可能接近您想要的异常。

如果需要捕获该特定错误,请使用一个名称。

I would inherit from ValueError

class IllegalArgumentError(ValueError):
    pass

It is sometimes better to create your own exceptions, but inherit from a built-in one, which is as close to what you want as possible.

If you need to catch that specific error, it is helpful to have a name.


回答 2

我认为处理此问题的最佳方法是python本身处理它的方法。Python引发TypeError。例如:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

我们的初级开发人员刚刚在Google搜索“ python异常错误参数”中找到了此页面,而令我惊讶的是,自问这个问题以来,十年来从未出现过明显的(对我而言)答案。

I think the best way to handle this is the way python itself handles it. Python raises a TypeError. For example:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

Our junior dev just found this page in a google search for “python exception wrong arguments” and I’m surprised that the obvious (to me) answer wasn’t ever suggested in the decade since this question was asked.


回答 3

我几乎只看到ValueError过这种情况下使用的内建函数。

I’ve mostly just seen the builtin ValueError used in this situation.


回答 4

这取决于参数的问题。

如果参数的类型错误,则引发TypeError。例如,当您获取字符串而不是这些布尔值之一时。

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

但是请注意,在Python中我们很少进行此类检查。如果参数确实无效,那么一些更深层的功能可能会为我们带来麻烦。而且,如果我们仅检查布尔值,也许某些代码用户以后会向其提供一个字符串,因为它知道非空字符串始终为True。这可能会救他一个演员。

如果参数包含无效值,请引发ValueError。这似乎更适合您的情况:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

或在此特定情况下,递归的True值表示保存的True值。由于我认为这是从错误中恢复,因此您可能还希望在日志中抱怨。

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

It depends on what the problem with the arguments is.

If the argument has the wrong type, raise a TypeError. For example, when you get a string instead of one of those Booleans.

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

Note, however, that in Python we rarely make any checks like this. If the argument really is invalid, some deeper function will probably do the complaining for us. And if we only check the boolean value, perhaps some code user will later just feed it a string knowing that non-empty strings are always True. It might save him a cast.

If the arguments have invalid values, raise ValueError. This seems more appropriate in your case:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

Or in this specific case, have a True value of recurse imply a True value of save. Since I would consider this a recovery from an error, you might also want to complain in the log.

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

回答 5

我不确定我是否同意继承ValueError-我对文档的解释ValueError应由内建函数引发…从中继承或自己引发它似乎不正确。

当内置操作或函数接收到类型正确但值不合适的参数时引发,并且这种情况没有通过诸如IndexError之类的更精确的异常描述。

ValueError异常文档

I’m not sure I agree with inheritance from ValueError — my interpretation of the documentation is that ValueError is only supposed to be raised by builtins… inheriting from it or raising it yourself seems incorrect.

Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.

ValueError documentation


回答 6

同意Markus关于提出自己的异常的建议,但是该异常的文字应阐明问题出在参数列表中,而不是单个参数值中。我建议:

class BadCallError(ValueError):
    pass

当缺少特定调用所需的关键字参数或参数值分别有效但彼此不一致时使用。 ValueError当特定参数是正确类型但超出范围时,仍将是正确的。

这不是Python中的标准exceptions吗?

总的来说,我希望Python样式在区分函数的错误输入(调用者的错误)和函数内部的错误结果(我的错误)方面更加犀利。因此,可能还存在BadArgumentError来区分参数中的值错误和本地变量中的值错误。

Agree with Markus’ suggestion to roll your own exception, but the text of the exception should clarify that the problem is in the argument list, not the individual argument values. I’d propose:

class BadCallError(ValueError):
    pass

Used when keyword arguments are missing that were required for the specific call, or argument values are individually valid but inconsistent with each other. ValueError would still be right when a specific argument is right type but out of range.

Shouldn’t this be a standard exception in Python?

In general, I’d like Python style to be a bit sharper in distinguishing bad inputs to a function (caller’s fault) from bad results within the function (my fault). So there might also be a BadArgumentError to distinguish value errors in arguments from value errors in locals.


python异常消息捕获

问题:python异常消息捕获

import ftplib
import urllib2
import os
import logging
logger = logging.getLogger('ftpuploader')
hdlr = logging.FileHandler('ftplog.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
FTPADDR = "some ftp address"

def upload_to_ftp(con, filepath):
    try:
        f = open(filepath,'rb')                # file to send
        con.storbinary('STOR '+ filepath, f)         # Send the file
        f.close()                                # Close file and FTP
        logger.info('File successfully uploaded to '+ FTPADDR)
    except, e:
        logger.error('Failed to upload to ftp: '+ str(e))

这似乎不起作用,出现语法错误,将所有类型的异常记录到文件中的正确方法是什么

import ftplib
import urllib2
import os
import logging
logger = logging.getLogger('ftpuploader')
hdlr = logging.FileHandler('ftplog.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
FTPADDR = "some ftp address"

def upload_to_ftp(con, filepath):
    try:
        f = open(filepath,'rb')                # file to send
        con.storbinary('STOR '+ filepath, f)         # Send the file
        f.close()                                # Close file and FTP
        logger.info('File successfully uploaded to '+ FTPADDR)
    except, e:
        logger.error('Failed to upload to ftp: '+ str(e))

This doesn’t seem to work, I get syntax error, what is the proper way of doing this for logging all kind of exceptions to a file


回答 0

您必须定义要捕获的异常类型。所以写except Exception, e:的,而不是except, e:一个普通的异常(即无论如何都会被记录)。

其他可能性是通过这种方式编写您的整个try / except代码:

try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to '+ FTPADDR)
except Exception, e: # work on python 2.x
    logger.error('Failed to upload to ftp: '+ str(e))

在Python 3.x和现代版本的Python 2.x中,使用except Exception as e代替except Exception, e

try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to '+ FTPADDR)
except Exception as e: # work on python 3.x
    logger.error('Failed to upload to ftp: '+ str(e))

You have to define which type of exception you want to catch. So write except Exception, e: instead of except, e: for a general exception (that will be logged anyway).

Other possibility is to write your whole try/except code this way:

try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to '+ FTPADDR)
except Exception, e: # work on python 2.x
    logger.error('Failed to upload to ftp: '+ str(e))

in Python 3.x and modern versions of Python 2.x use except Exception as e instead of except Exception, e:

try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to '+ FTPADDR)
except Exception as e: # work on python 3.x
    logger.error('Failed to upload to ftp: '+ str(e))

回答 1

python 3不再支持该语法。请改用以下内容。

try:
    do_something()
except BaseException as e:
    logger.error('Failed to do something: ' + str(e))

The syntax is no longer supported in python 3. Use the following instead.

try:
    do_something()
except BaseException as e:
    logger.error('Failed to do something: ' + str(e))

回答 2

将其更新为更简单的记录器(适用于python 2和3)。您不需要回溯模块。

import logging

logger = logging.Logger('catch_all')

def catchEverythingInLog():
    try:
        ... do something ...
    except Exception as e:
        logger.error(e, exc_info=True)
        ... exception handling ...

现在这是旧方法(尽管仍然有效):

import sys, traceback

def catchEverything():
    try:
        ... some operation(s) ...
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        ... exception handling ...

exc_value是错误消息。

Updating this to something simpler for logger (works for both python 2 and 3). You do not need traceback module.

import logging

logger = logging.Logger('catch_all')

def catchEverythingInLog():
    try:
        ... do something ...
    except Exception as e:
        logger.error(e, exc_info=True)
        ... exception handling ...

This is now the old way (though still works):

import sys, traceback

def catchEverything():
    try:
        ... some operation(s) ...
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        ... exception handling ...

exc_value is the error message.


回答 3

在某些情况下,您可以使用e.messagee.messages ..但是,并非在所有情况下都有效。无论如何,使用str(e)更安全

try:
  ...
except Exception as e:
  print(e.message)

There are some cases where you can use the e.message or e.messages.. But it does not work in all cases. Anyway the more safe is to use the str(e)

try:
  ...
except Exception as e:
  print(e.message)

回答 4

如果您需要错误类,错误消息和堆栈跟踪(或其中一些),请使用sys.exec_info()

带有某些格式的最少工作代码:

import sys
import traceback

try:
    ans = 1/0
except BaseException as ex:
    # Get current system exception
    ex_type, ex_value, ex_traceback = sys.exc_info()

    # Extract unformatter stack traces as tuples
    trace_back = traceback.extract_tb(ex_traceback)

    # Format stacktrace
    stack_trace = list()

    for trace in trace_back:
        stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))

    print("Exception type : %s " % ex_type.__name__)
    print("Exception message : %s" %ex_value)
    print("Stack trace : %s" %stack_trace)

给出以下输出:

Exception type : ZeroDivisionError
Exception message : division by zero
Stack trace : ['File : .\\test.py , Line : 5, Func.Name : <module>, Message : ans = 1/0']

函数sys.exc_info()为您提供有关最新异常的详细信息。返回的元组(type, value, traceback)

traceback是回溯对象的实例。您可以使用提供的方法来格式化跟踪。在追溯文档中可以找到更多内容

If you want the error class, error message and stack trace (or some of those), use sys.exec_info().

Minimal working code with some formatting:

import sys
import traceback

try:
    ans = 1/0
except BaseException as ex:
    # Get current system exception
    ex_type, ex_value, ex_traceback = sys.exc_info()

    # Extract unformatter stack traces as tuples
    trace_back = traceback.extract_tb(ex_traceback)

    # Format stacktrace
    stack_trace = list()

    for trace in trace_back:
        stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))

    print("Exception type : %s " % ex_type.__name__)
    print("Exception message : %s" %ex_value)
    print("Stack trace : %s" %stack_trace)

Which gives the following output:

Exception type : ZeroDivisionError
Exception message : division by zero
Stack trace : ['File : .\\test.py , Line : 5, Func.Name : <module>, Message : ans = 1/0']

The function sys.exc_info() gives you details about the most recent exception. It returns a tuple of (type, value, traceback).

traceback is an instance of traceback object. You can format the trace with the methods provided. More can be found in the traceback documentation .


回答 5

您可以使用logger.exception("msg")traceback记录异常:

try:
    #your code
except Exception as e:
    logger.exception('Failed: ' + str(e))

You can use logger.exception("msg") for logging exception with traceback:

try:
    #your code
except Exception as e:
    logger.exception('Failed: ' + str(e))

回答 6

在python 3.6之后,您可以使用格式化的字符串文字。干净利落!(https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498

try
 ...
except Exception as e:
    logger.error(f"Failed to upload to ftp: {e}")

After python 3.6, you can use formatted string literal. It’s neat! (https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498)

try
 ...
except Exception as e:
    logger.error(f"Failed to upload to ftp: {e}")

回答 7

您可以尝试明确指定BaseException类型。但是,这只会捕获BaseException的派生类。尽管这包括所有实现提供的异常,但也可能会引发任意旧式类。

try:
  do_something()
except BaseException, e:
  logger.error('Failed to do something: ' + str(e))

You can try specifying the BaseException type explicitly. However, this will only catch derivatives of BaseException. While this includes all implementation-provided exceptions, it is also possibly to raise arbitrary old-style classes.

try:
  do_something()
except BaseException, e:
  logger.error('Failed to do something: ' + str(e))

回答 8

使用str(ex)打印执行

try:
   #your code
except ex:
   print(str(ex))

Use str(ex) to print execption

try:
   #your code
except ex:
   print(str(ex))

回答 9

对于未来的奋斗者,在python 3.8.2(可能还有之前的几个版本)中,语法为

except Attribute as e:
    print(e)

for the future strugglers, in python 3.8.2(and maybe a few versions before that), the syntax is

except Attribute as e:
    print(e)

回答 10

使用str(e)repr(e)表示异常,您将无法获得实际的堆栈跟踪,因此查找异常在哪里没有帮助。

阅读其他答案和日志记录包doc之后,以下两种方法可以很好地打印实际的堆栈跟踪信息,以便于调试:

logger.debug()与参数一起使用exc_info

try:
    # my code
exception SomeError as e:
    logger.debug(e, exc_info=True)

采用 logger.exception()

或者我们可以直接使用它logger.exception()来打印异常。

try:
    # my code
exception SomeError as e:
    logger.exception(e)

Using str(e) or repr(e) to represent the exception, you won’t get the actual stack trace, so it is not helpful to find where the exception is.

After reading other answers and the logging package doc, the following two ways works great to print the actual stack trace for easier debugging:

use logger.debug() with parameter exc_info

try:
    # my code
exception SomeError as e:
    logger.debug(e, exc_info=True)

use logger.exception()

or we can directly use logger.exception() to print the exception.

try:
    # my code
exception SomeError as e:
    logger.exception(e)

如何记录带有调试信息的Python错误?

问题:如何记录带有调试信息的Python错误?

我正在使用以下命令将Python异常消息打印到日志文件中logging.error

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

除了异常字符串以外,是否可以打印有关异常及其生成代码的更多详细信息?行号或堆栈跟踪之类的东西会很棒。

I am printing Python exception messages to a log file with logging.error:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

Is it possible to print more detailed information about the exception and the code that generated it than just the exception string? Things like line numbers or stack traces would be great.


回答 0

logger.exception 将在错误消息旁边输出堆栈跟踪。

例如:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

输出:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Check指出:“请注意,在Python 3中,您必须logging.exceptionexcept零件内部调用该方法。如果在任意位置调用此方法,则可能会遇到奇怪的异常。文档对此有所提示。”

logger.exception will output a stack trace alongside the error message.

For example:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

Output:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Cheque notes, “be aware that in Python 3 you must call the logging.exception method just inside the except part. If you call this method in an arbitrary place you may get a bizarre exception. The docs alert about that.”


回答 1

约一个好处logging.exceptionSiggyF的回答并没有显示的是,你可以在任意的消息传递和记录仍然会显示完整的回溯与所有异常的详细信息:

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

在默认情况下(在最新版本中),仅将错误打印到的日志记录行为sys.stderr如下所示:

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

One nice thing about logging.exception that SiggyF’s answer doesn’t show is that you can pass in an arbitrary message, and logging will still show the full traceback with all the exception details:

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

With the default (in recent versions) logging behaviour of just printing errors to sys.stderr, it looks like this:

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

回答 2

使用exc_info选项可能更好,允许您选择错误级别(如果使用exception,它将始终处于错误error级别):

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

Using exc_info options may be better, to allow you to choose the error level (if you use exception, it will always be at the error level):

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

回答 3

报价单

如果您的应用程序以其他方式记录日志而不使用logging模块怎么办?

现在,traceback可以在这里使用。

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Python 2中使用它:

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
  • Python 3中使用它:

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)

Quoting

What if your application does logging some other way – not using the logging module?

Now, traceback could be used here.

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Use it in Python 2:

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
    
  • Use it in Python 3:

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)
    

回答 4

如果您使用纯日志-您的所有日志记录都应符合以下规则:one record = one line。遵循此规则,您可以使用grep和其他工具来处理日志文件。

但是回溯信息是多行的。因此,我的答案是zangw在此线程中提出的解决方案的扩展版本。问题是回溯线可能在\n内部,因此我们需要做一些额外的工作来消除该行的结尾:

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

之后(当您要分析日志时),您可以从日志文件中复制/粘贴所需的回溯行,然后执行以下操作:

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

利润!

If you use plain logs – all your log records should correspond this rule: one record = one line. Following this rule you can use grep and other tools to process your log files.

But traceback information is multi-line. So my answer is an extended version of solution proposed by zangw above in this thread. The problem is that traceback lines could have \n inside, so we need to do an extra work to get rid of this line endings:

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

After that (when you’ll be analyzing your logs) you could copy / paste required traceback lines from your log file and do this:

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

Profit!


回答 5

这个答案是建立在上述优秀答案之上的。

在大多数应用程序中,您不会直接调用logging.exception(e)。您很可能已经定义了特定于您的应用程序或模块的自定义记录器,如下所示:

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

在这种情况下,只需使用记录器调用异常(e),如下所示:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

This answer builds up from the above excellent ones.

In most applications, you won’t be calling logging.exception(e) directly. Most likely you have defined a custom logger specific for your application or module like this:

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

In this case, just use the logger to call the exception(e) like this:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

回答 6

您可以毫无exceptions地记录堆栈跟踪。

https://docs.python.org/3/library/logging.html#logging.Logger.debug

第二个可选的关键字参数是stack_info,默认为False。如果为true,则将堆栈信息添加到日志消息中,包括实际的日志调用。请注意,这与通过指定exc_info显示的堆栈信息不同:前者是从堆栈底部到当前线程中的日志记录调用的堆栈帧,而后者是有关已取消缠绕的堆栈帧的信息,在搜索异常处理程序时跟踪异常。

例:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

You can log the stack trace without an exception.

https://docs.python.org/3/library/logging.html#logging.Logger.debug

The second optional keyword argument is stack_info, which defaults to False. If true, stack information is added to the logging message, including the actual logging call. Note that this is not the same stack information as that displayed through specifying exc_info: The former is stack frames from the bottom of the stack up to the logging call in the current thread, whereas the latter is information about stack frames which have been unwound, following an exception, while searching for exception handlers.

Example:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

回答 7

一点点装饰器处理(受Maybe monad和举重的启发很松散)。您可以安全地删除Python 3.6类型注释,并使用较旧的消息格式样式。

fallable.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

演示:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

您还可以修改此解决方案来回报比的东西更有意义一点Noneexcept部分(甚至使溶液一般,通过指定该返回值fallible的论点)。

A little bit of decorator treatment (very loosely inspired by the Maybe monad and lifting). You can safely remove Python 3.6 type annotations and use an older message formatting style.

fallible.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

Demo:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

You can also modify this solution to return something a bit more meaningful than None from the except part (or even make the solution generic, by specifying this return value in fallible‘s arguments).


回答 8

在您的日志记录模块(如果是自定义模块)中,只需启用stack_info即可。

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

In your logging module(if custom module) just enable stack_info.

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

回答 9

如果您可以处理额外的依赖关系,则可以使用twisted.log,您不必显式记录错误,而且它会将整个回溯和时间返回到文件或流。

If you can cope with the extra dependency then use twisted.log, you don’t have to explicitly log errors and also it returns the entire traceback and time to the file or stream.


回答 10

一种干净的方法是使用format_exc(),然后解析输出以获取相关部分:

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

问候

A clean way to do it is using format_exc() and then parse the output to get the relevant part:

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

Regards