标签归档:exception

在Python中使用try-except-else是否是一种好习惯?

问题:在Python中使用try-except-else是否是一种好习惯?

在Python中,我不时看到该块:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

try-except-else存在的原因是什么?

我不喜欢这种编程,因为它使用异常来执行流控制。但是,如果它包含在语言中,则一定有充分的理由,不是吗?

据我了解,异常不是错误,并且仅应将其用于特殊情况(例如,我尝试将文件写入磁盘,并且没有更多空间,或者我没有权限),而不是流控制。

通常,我将异常处理为:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

或者,如果发生异常,我真的不想返回任何东西,那么:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

From time to time in Python, I see the block:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

What is the reason for the try-except-else to exist?

I do not like that kind of programming, as it is using exceptions to perform flow control. However, if it is included in the language, there must be a good reason for it, isn’t it?

It is my understanding that exceptions are not errors, and that they should only be used for exceptional conditions (e.g. I try to write a file into disk and there is no more space, or maybe I do not have permission), and not for flow control.

Normally I handle exceptions as:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

Or if I really do not want to return anything if an exception happens, then:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

回答 0

“我不知道它是否出于无知,但我不喜欢这种编程,因为它使用异常来执行流控制。”

在Python世界中,使用异常进行流控制是常见且正常的。

甚至Python核心开发人员也将异常用于流控制,并且该样式已在语言中大量使用(即,迭代器协议使用StopIteration发出信号以终止循环)。

此外,try-except样式用于防止某些“跨越式”构造固有的竞争条件。例如,测试os.path.exists会导致信息在您使用时已过时。同样,Queue.full返回的信息可能已过时。在这种情况下,try-except-else样式将产生更可靠的代码。

“据我了解,异常不是错误,它们仅应用于特殊情况”

在其他一些语言中,该规则反映了图书馆所反映的文化规范。该“规则”还部分基于这些语言的性能考虑。

Python的文化规范有些不同。在许多情况下,必须对控制流使用exceptions。另外,在Python中使用异常不会像在某些编译语言中那样降低周围的代码和调用代码的速度(即CPython已经在每一步实现了用于异常检查的代码,而不管您是否实际使用异常)。

换句话说,您理解“exceptions是为了exceptions”是一条在其他语言中有意义的规则,但不适用于Python。

“但是,如果它本身包含在语言中,那一定有充分的理由,不是吗?”

除了帮助避免竞争条件外,异常对于在循环外拉出错误处理也非常有用。这是解释语言中的必要优化,这些语言通常不会具有自动循环不变的代码运动

另外,在通常情况下,异常可以大大简化代码,在正常情况下,处理问题的能力与问题发生的地方相距甚远。例如,通常有用于业务逻辑的顶级用户界面代码调用代码,而后者又调用低级例程。低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能以顶级代码处理(例如,要求用户提供与现有键不冲突的新键)。对此类控制流使用异常可以使中级例程完全忽略该问题,并将其与流控制的这一方面很好地分离。

这里有一篇关于异常必不可少的不错的博客文章

另外,请参见此堆栈溢出答案:异常真的是异常错误吗?

“ try-except-else存在的原因是什么?”

其他条款本身很有趣。它在没有exceptions的情况下运行,但是在最终条款之前。这是其主要目的。

如果没有else子句,那么在最终确定之前运行其他代码的唯一选择就是将代码添加到try子句的笨拙做法。这很笨拙,因为它冒着在代码中引发异常的危险,而这些异常本来不会受到try块的保护。

在完成之前运行其他不受保护的代码的用例很少出现。因此,不要期望在已发布的代码中看到很多示例。这有点罕见。

else子句的另一个用例是执行在没有异常发生时必须发生的动作以及在处理异常时不发生的动作。例如:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

另一个示例发生在单元测试赛跑者中:

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

最后,在尝试块中最常用的else子句是为了美化一些(在相同的缩进级别上对齐exceptions结果和非exceptions结果)。此用法始终是可选的,并非严格必要。

“I do not know if it is out of ignorance, but I do not like that kind of programming, as it is using exceptions to perform flow control.”

In the Python world, using exceptions for flow control is common and normal.

Even the Python core developers use exceptions for flow-control and that style is heavily baked into the language (i.e. the iterator protocol uses StopIteration to signal loop termination).

In addition, the try-except-style is used to prevent the race-conditions inherent in some of the “look-before-you-leap” constructs. For example, testing os.path.exists results in information that may be out-of-date by the time you use it. Likewise, Queue.full returns information that may be stale. The try-except-else style will produce more reliable code in these cases.

“It my understanding that exceptions are not errors, they should only be used for exceptional conditions”

In some other languages, that rule reflects their cultural norms as reflected in their libraries. The “rule” is also based in-part on performance considerations for those languages.

The Python cultural norm is somewhat different. In many cases, you must use exceptions for control-flow. Also, the use of exceptions in Python does not slow the surrounding code and calling code as it does in some compiled languages (i.e. CPython already implements code for exception checking at every step, regardless of whether you actually use exceptions or not).

In other words, your understanding that “exceptions are for the exceptional” is a rule that makes sense in some other languages, but not for Python.

“However, if it is included in the language itself, there must be a good reason for it, isn’t it?”

Besides helping to avoid race-conditions, exceptions are also very useful for pulling error-handling outside loops. This is a necessary optimization in interpreted languages which do not tend to have automatic loop invariant code motion.

Also, exceptions can simplify code quite a bit in common situations where the ability to handle an issue is far removed from where the issue arose. For example, it is common to have top level user-interface code calling code for business logic which in turn calls low-level routines. Situations arising in the low-level routines (such as duplicate records for unique keys in database accesses) can only be handled in top-level code (such as asking the user for a new key that doesn’t conflict with existing keys). The use of exceptions for this kind of control-flow allows the mid-level routines to completely ignore the issue and be nicely decoupled from that aspect of flow-control.

There is a nice blog post on the indispensibility of exceptions here.

Also, see this Stack Overflow answer: Are exceptions really for exceptional errors?

“What is the reason for the try-except-else to exist?”

The else-clause itself is interesting. It runs when there is no exception but before the finally-clause. That is its primary purpose.

Without the else-clause, the only option to run additional code before finalization would be the clumsy practice of adding the code to the try-clause. That is clumsy because it risks raising exceptions in code that wasn’t intended to be protected by the try-block.

The use-case of running additional unprotected code prior to finalization doesn’t arise very often. So, don’t expect to see many examples in published code. It is somewhat rare.

Another use-case for the else-clause is to perform actions that must occur when no exception occurs and that do not occur when exceptions are handled. For example:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

Another example occurs in unittest runners:

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

Lastly, the most common use of an else-clause in a try-block is for a bit of beautification (aligning the exceptional outcomes and non-exceptional outcomes at the same level of indentation). This use is always optional and isn’t strictly necessary.


回答 1

try-except-else存在的原因是什么?

一个try块可以处理预期的错误。该except块应该只捕获您准备处理的异常。如果您处理了意外错误,则您的代码可能会做错事情并隐藏错误。

else如果没有错误,将执行一个子句,通过不执行该代码try块中的代码,可以避免捕获意外错误。同样,捕获意外错误可能会隐藏错误。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

“ try,except”套件有两个可选子句,elsefinally。所以实际上是try-except-else-finally

else仅在try块中没有异常的情况下才会评估。它使我们能够简化下面更复杂的代码:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

因此,如果将a else与替代方案(可能会产生错误)进行比较,我们会发现它减少了代码行,并且我们可以拥有更具可读性,可维护性和更少错误的代码库。

finally

finally 即使使用return语句对另一行进行评估,它也将执行。

用伪代码分解

这可能有助于以尽可能小的形式展示所有功能并带有注释来分解此内容。假定此语法在语法上正确(但除非定义名称,否则不可运行)伪代码在函数中。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

的确,我们可以将代码包含在else块中的代码中try,如果没有异常,它将在其中运行,但是如果该代码本身引发了我们正在捕获的异常,该怎么办?将其留在try块中将隐藏该错误。

我们希望最小化try块中的代码行,以避免捕获我们未曾想到的异常,其原理是,如果我们的代码失败,我们希望它大声失败。这是最佳做法

据我了解,异常不是错误

在Python中,大多数exceptions都是错误。

我们可以使用pydoc查看异常层次结构。例如,在Python 2中:

$ python -m pydoc exceptions

或Python 3:

$ python -m pydoc builtins

将给我们层次结构。我们可以看到大多数Exception错误都是错误的,尽管Python使用其中的一些错误来结束for循环(StopIteration)。这是Python 3的层次结构:

BaseException
    Exception
        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
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

有评论者问:

假设您有一个可对外部API进行ping的方法,并且想在API包装器之外的类上处理异常,那么您是否只是从方法中的except子句中返回e,其中e是异常对象?

不,您不返回该异常,只需将其重新引发raise以保留堆栈跟踪即可。

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

或者,在Python 3中,您可以引发新的异常并通过异常链接保留回溯:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

我在这里详细回答

What is the reason for the try-except-else to exist?

A try block allows you to handle an expected error. The except block should only catch exceptions you are prepared to handle. If you handle an unexpected error, your code may do the wrong thing and hide bugs.

An else clause will execute if there were no errors, and by not executing that code in the try block, you avoid catching an unexpected error. Again, catching an unexpected error can hide bugs.

Example

For example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

The “try, except” suite has two optional clauses, else and finally. So it’s actually try-except-else-finally.

else will evaluate only if there is no exception from the try block. It allows us to simplify the more complicated code below:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

so if we compare an else to the alternative (which might create bugs) we see that it reduces the lines of code and we can have a more readable, maintainable, and less buggy code-base.

finally

finally will execute no matter what, even if another line is being evaluated with a return statement.

Broken down with pseudo-code

It might help to break this down, in the smallest possible form that demonstrates all features, with comments. Assume this syntactically correct (but not runnable unless the names are defined) pseudo-code is in a function.

For example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

It is true that we could include the code in the else block in the try block instead, where it would run if there were no exceptions, but what if that code itself raises an exception of the kind we’re catching? Leaving it in the try block would hide that bug.

We want to minimize lines of code in the try block to avoid catching exceptions we did not expect, under the principle that if our code fails, we want it to fail loudly. This is a best practice.

It is my understanding that exceptions are not errors

In Python, most exceptions are errors.

We can view the exception hierarchy by using pydoc. For example, in Python 2:

$ python -m pydoc exceptions

or Python 3:

$ python -m pydoc builtins

Will give us the hierarchy. We can see that most kinds of Exception are errors, although Python uses some of them for things like ending for loops (StopIteration). This is Python 3’s hierarchy:

BaseException
    Exception
        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
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

A commenter asked:

Say you have a method which pings an external API and you want to handle the exception at a class outside the API wrapper, do you simply return e from the method under the except clause where e is the exception object?

No, you don’t return the exception, just reraise it with a bare raise to preserve the stacktrace.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

Or, in Python 3, you can raise a new exception and preserve the backtrace with exception chaining:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

I elaborate in my answer here.


回答 2

Python不赞成将异常仅用于特殊情况的想法,实际上,习惯用法是“要求宽恕,而不是允许”。这意味着将异常作为流程控制的常规部分是完全可以接受的,并且实际上是受到鼓励的。

通常,这是一件好事,因为以这种方式工作有助于避免某些问题(显而易见的示例是,通常避免出现竞争条件),并且它倾向于使代码更具可读性。

假设您遇到这样一种情况,您需要处理一些用户输入,但是已经处理了默认输入。该try: ... except: ... else: ...结构使代码易于阅读:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

与其他语言可能的工作方式进行比较:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

注意优点。无需检查该值是否有效并单独对其进行分析,只需完成一次即可。代码也遵循更合理的顺序,首先是主代码路径,然后是“如果不起作用,请执行此操作”。

该示例自然有点虚构,但它显示了这种结构的情况。

Python doesn’t subscribe to the idea that exceptions should only be used for exceptional cases, in fact the idiom is ‘ask for forgiveness, not permission’. This means that using exceptions as a routine part of your flow control is perfectly acceptable, and in fact, encouraged.

This is generally a good thing, as working this way helps avoid some issues (as an obvious example, race conditions are often avoided), and it tends to make code a little more readable.

Imagine you have a situation where you take some user input which needs to be processed, but have a default which is already processed. The try: ... except: ... else: ... structure makes for very readable code:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

Compare to how it might work in other languages:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

Note the advantages. There is no need to check the value is valid and parse it separately, they are done once. The code also follows a more logical progression, the main code path is first, followed by ‘if it doesn’t work, do this’.

The example is naturally a little contrived, but it shows there are cases for this structure.


回答 3

在python中使用try-except-else是否是一种好习惯?

答案是它取决于上下文。如果您这样做:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

它表明您不太了解Python。此功能封装在dict.get方法中:

item = d.get('item', 'default')

try/ except块是写什么都可以有效地在一行用原子方法执行的视觉上更多混乱和冗长的方式。在其他情况下,这是正确的。

但是,这并不意味着我们应该避免所有异常处理。在某些情况下,最好避免比赛条件。不要检查文件是否存在,只需尝试将其打开,然后捕获相应的IOError。为了简单起见,请尝试将其封装或分解为适当的名称。

阅读PythonZen,了解其中存在一些紧绷的原则,并且要警惕过于依赖其中任何一条语句的教条。

Is it a good practice to use try-except-else in python?

The answer to this is that it is context dependent. If you do this:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

It demonstrates that you don’t know Python very well. This functionality is encapsulated in the dict.get method:

item = d.get('item', 'default')

The try/except block is a much more visually cluttered and verbose way of writing what can be efficiently executing in a single line with an atomic method. There are other cases where this is true.

However, that does not mean that we should avoid all exception handling. In some cases it is preferred to avoid race conditions. Don’t check if a file exists, just attempt to open it, and catch the appropriate IOError. For the sake of simplicity and readability, try to encapsulate this or factor it out as apropos.

Read the Zen of Python, understanding that there are principles that are in tension, and be wary of dogma that relies too heavily on any one of the statements in it.


回答 4

请参见以下示例,该示例说明了有关try-except-else-finally的所有信息:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

实施它并获得:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

See the following example which illustrate everything about try-except-else-finally:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

Implement it and come by:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

回答 5

您应谨慎使用finally块,因为它与try中使用else块的功能不同,除了。无论try的结果如何,都将运行finally块。

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

正如所有人都指出的那样,使用else块会使您的代码更具可读性,并且仅在未引发异常时运行

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

You should be careful about using the finally block, as it is not the same thing as using an else block in the try, except. The finally block will be run regardless of the outcome of the try except.

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

As everyone has noted using the else block causes your code to be more readable, and only runs when an exception is not thrown

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

回答 6

每当您看到以下内容时:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

甚至这个:

try:
    return 1 / x
except ZeroDivisionError:
    return None

考虑一下这个:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

Whenever you see this:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

Or even this:

try:
    return 1 / x
except ZeroDivisionError:
    return None

Consider this instead:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

回答 7

只是因为没有人发表过这一意见,我会说

避免使用else条款,因为大多数人都不熟悉这些条款try/excepts

与关键字tryexcept和和不同finally,该else子句的含义不言而喻。它的可读性较差。因为它不经常使用,所以它将导致阅读您的代码的人想要仔细检查文档,以确保他们了解正在发生的事情。

(我之所以写此答案,恰恰是因为我try/except/else在代码库中找到了a ,它导致了wtf时刻并迫使我进行了谷歌搜索)。

因此,无论我在哪里看到类似OP示例的代码:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

我宁愿重构为

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1>明确的回报清楚地表明,在exceptions情况下,我们已经完成工作

  • <2>作为一个很好的次要副作用,该else块中的代码以前经过了一个级别的确定。

Just because no-one else has posted this opinion, I would say

avoid else clauses in try/excepts because they’re unfamiliar to most people

Unlike the keywords try, except, and finally, the meaning of the else clause isn’t self-evident; it’s less readable. Because it’s not used very often, it’ll cause people that read your code to want to double-check the docs to be sure they understand what’s going on.

(I’m writing this answer precisely because I found a try/except/else in my codebase and it caused a wtf moment and forced me to do some googling).

So, wherever I see code like the OP example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

I would prefer to refactor to

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1> explicit return, clearly shows that, in the exception case, we are finished working

  • <2> as a nice minor side-effect, the code that used to be in the else block is dedented by one level.


回答 8

这是我关于如何理解Python中try-except-else-finally块的简单代码段:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

让我们尝试div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

让我们尝试div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

This is my simple snippet on howto understand try-except-else-finally block in Python:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

Let’s try div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

Let’s try div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

回答 9

OP,您是正确的。 在Python中try / except之后的else很难看。它导致另一个不需要的流控制对象:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

完全清楚的等效项是:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

这比else子句清楚得多。try / except之后的else不经常编写,因此花一点时间弄清楚其含义是什么。

仅仅因为您可以做某事,并不意味着您应该做某事。

语言已经添加了许多功能,因为有人认为它可能派上用场。麻烦的是,功能越多,事物的清晰度和显而易见性就越差,这是因为人们通常不使用那些钟声和口哨声。

这里只有我的5美分。我必须走到后面,清理掉大学一年级开发人员写的很多代码,这些开发人员认为他们很聪明,并希望以超级严格,超级高效的方式编写代码,这只会使事情变得一团糟以尝试稍后阅读/修改。我每天对可读性进行投票,而星期日则两次。

OP, YOU ARE CORRECT. The else after try/except in Python is ugly. it leads to another flow-control object where none is needed:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

A totally clear equivalent is:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

This is far clearer than an else clause. The else after try/except is not frequently written, so it takes a moment to figure what the implications are.

Just because you CAN do a thing, doesn’t mean you SHOULD do a thing.

Lots of features have been added to languages because someone thought it might come in handy. Trouble is, the more features, the less clear and obvious things are because people don’t usually use those bells and whistles.

Just my 5 cents here. I have to come along behind and clean up a lot of code written by 1st-year out of college developers who think they’re smart and want to write code in some uber-tight, uber-efficient way when that just makes it a mess to try and read / modify later. I vote for readability every day and twice on Sundays.


如何检查变量是否存在?

问题:如何检查变量是否存在?

我想检查一个变量是否存在。现在我正在做这样的事情:

try:
   myVar
except NameError:
   # Do something.

是否有其他方法无一exceptions?

I want to check if a variable exists. Now I’m doing something like this:

try:
   myVar
except NameError:
   # Do something.

Are there other ways without exceptions?


回答 0

要检查是否存在局部变量:

if 'myVar' in locals():
  # myVar exists.

要检查是否存在全局变量:

if 'myVar' in globals():
  # myVar exists.

要检查对象是否具有属性:

if hasattr(obj, 'attr_name'):
  # obj.attr_name exists.

To check the existence of a local variable:

if 'myVar' in locals():
  # myVar exists.

To check the existence of a global variable:

if 'myVar' in globals():
  # myVar exists.

To check if an object has an attribute:

if hasattr(obj, 'attr_name'):
  # obj.attr_name exists.

回答 1

使用中那些尚未被定义或组(或明或暗地)变量几乎总是一件坏事任何语言,因为这往往预示着该计划的逻辑还没有被恰当地考虑,并有可能的结果行为无法预测。

如果您需要在Python中执行此操作,以下与您的操作类似的技巧将确保变量在使用前具有一定的价值:

try:
    myVar
except NameError:
    myVar = None

# Now you're free to use myVar without Python complaining.

但是,我仍然不认为这是个好主意-在我看来,您应该尝试重构代码,以免发生这种情况。

The use of variables that have yet to been defined or set (implicitly or explicitly) is almost always a bad thing in any language, since it often indicates that the logic of the program hasn’t been thought through properly, and is likely to result in unpredictable behaviour.

If you need to do it in Python, the following trick, which is similar to yours, will ensure that a variable has some value before use:

try:
    myVar
except NameError:
    myVar = None

# Now you're free to use myVar without Python complaining.

However, I’m still not convinced that’s a good idea – in my opinion, you should try to refactor your code so that this situation does not occur.


回答 2

一种简单的方法是一开始就初始化它 myVar = None

然后稍后:

if myVar is not None:
    # Do something

A simple way is to initialize it at first saying myVar = None

Then later on:

if myVar is not None:
    # Do something

回答 3

使用try / except是测试变量是否存在的最佳方法。但是几乎可以肯定,有一种比设置/测试全局变量更好的方法。

例如,如果您想在第一次调用某个函数时初始化模块级变量,那么最好使用如下代码:

my_variable = None

def InitMyVariable():
  global my_variable
  if my_variable is None:
    my_variable = ...

Using try/except is the best way to test for a variable’s existence. But there’s almost certainly a better way of doing whatever it is you’re doing than setting/testing global variables.

For example, if you want to initialize a module-level variable the first time you call some function, you’re better off with code something like this:

my_variable = None

def InitMyVariable():
  global my_variable
  if my_variable is None:
    my_variable = ...

回答 4

对于对象/模块,您还可以

'var' in dir(obj)

例如,

>>> class Something(object):
...     pass
...
>>> c = Something()
>>> c.a = 1
>>> 'a' in dir(c)
True
>>> 'b' in dir(c)
False

for objects/modules, you can also

'var' in dir(obj)

For example,

>>> class Something(object):
...     pass
...
>>> c = Something()
>>> c.a = 1
>>> 'a' in dir(c)
True
>>> 'b' in dir(c)
False

回答 5

我将假定该测试将在功能中使用,类似于user97370的答案。我不喜欢这个答案,因为它污染了全局命名空间。解决该问题的一种方法是改用类:

class InitMyVariable(object):
  my_variable = None

def __call__(self):
  if self.my_variable is None:
   self.my_variable = ...

我不喜欢这样,因为它使代码复杂化,并提出了一些问题,例如,是否应该确认Singleton编程模式?幸运的是,Python允许函数在一段时间内拥有属性,这为我们提供了一个简单的解决方案:

def InitMyVariable():
  if InitMyVariable.my_variable is None:
    InitMyVariable.my_variable = ...
InitMyVariable.my_variable = None

I will assume that the test is going to be used in a function, similar to user97370’s answer. I don’t like that answer because it pollutes the global namespace. One way to fix it is to use a class instead:

class InitMyVariable(object):
  my_variable = None

def __call__(self):
  if self.my_variable is None:
   self.my_variable = ...

I don’t like this, because it complicates the code and opens up questions such as, should this confirm to the Singleton programming pattern? Fortunately, Python has allowed functions to have attributes for a while, which gives us this simple solution:

def InitMyVariable():
  if InitMyVariable.my_variable is None:
    InitMyVariable.my_variable = ...
InitMyVariable.my_variable = None

回答 6

catchexcept在Python中被称为。除此之外,对于这种简单情况也很好。还有的AttributeError,可以用来检查一个对象具有的属性。

catch is called except in Python. other than that it’s fine for such simple cases. There’s the AttributeError that can be used to check if an object has an attribute.


回答 7

处理这种情况的一种通常有效的方法是不显式检查变量是否存在,而只是继续将可能不存在的变量的首次用法包装在try / except NameError中:

# Search for entry.
for x in y:
  if x == 3:
    found = x

# Work with found entry.
try:
  print('Found: {0}'.format(found))
except NameError:
  print('Not found')
else:
  # Handle rest of Found case here
  ...

A way that often works well for handling this kind of situation is to not explicitly check if the variable exists but just go ahead and wrap the first usage of the possibly non-existing variable in a try/except NameError:

# Search for entry.
for x in y:
  if x == 3:
    found = x

# Work with found entry.
try:
  print('Found: {0}'.format(found))
except NameError:
  print('Not found')
else:
  # Handle rest of Found case here
  ...

回答 8

我创建了一个自定义函数。

def exists(var):
     var_exists = var in locals() or var in globals()
     return var_exists

然后调用如下函数,将其替换variable_name为要检查的变量:

exists("variable_name")

将返回TrueFalse

I created a custom function.

def exists(var):
     var_exists = var in locals() or var in globals()
     return var_exists

Then the call the function like follows replacing variable_name with the variable you want to check:

exists("variable_name")

Will return True or False


您如何测试Python函数引发异常?

问题:您如何测试Python函数引发异常?

如何编写仅在函数未引发预期异常的情况下失败的单元测试?

How does one write a unittest that fails only if a function doesn’t throw an expected exception?


回答 0

使用unittest模块中的TestCase.assertRaises(或TestCase.failUnlessRaises),例如:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

回答 1

从Python 2.7开始,您可以使用上下文管理器来获取抛出的实际Exception对象:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


Python的3.5,你必须包装context.exceptionstr,否则,你会得到一个TypeError

self.assertTrue('This is broken' in str(context.exception))

Since Python 2.7 you can use context manager to get ahold of the actual Exception object thrown:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


In Python 3.5, you have to wrap context.exception in str, otherwise you’ll get a TypeError

self.assertTrue('This is broken' in str(context.exception))

回答 2

我上一个答案中的代码可以简化为:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

如果函数接受参数,则将它们传递给assertRaises,如下所示:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

The code in my previous answer can be simplified to:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

And if afunction takes arguments, just pass them into assertRaises like this:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

回答 3

您如何测试Python函数引发异常?

如何编写仅在函数未引发预期异常的情况下失败的测试?

简短答案:

将该self.assertRaises方法用作上下文管理器:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

示范

最佳实践方法相当容易在Python Shell中进行演示。

unittest

在Python 2.7或3中:

import unittest

在Python 2.6中,您可以安装2.7 unittest库的向后移植,称为unittest2,并将其别名为unittest

import unittest2 as unittest

测试示例

现在,将以下Python类型安全性测试粘贴到您的Python Shell中:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

测试人员assertRaises用作上下文管理器,以确保在记录错误的同时正确捕获并清除该错误。

我们也可以使用上下文管理器来编写它,请参阅测试二。第一个参数是您希望引发的错误类型,第二个参数是您要测试的函数,其余的args和关键字args将传递给该函数。

我认为仅使用上下文管理器就更加简单,可读性和可维护性。

运行测试

要运行测试:

unittest.main(exit=False)

在Python 2.6中,您可能需要以下内容

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

并且您的终端应输出以下内容:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

然后,正如我们期望的那样,尝试在中添加1'1'结果TypeError


有关更详细的输出,请尝试以下操作:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

How do you test that a Python function throws an exception?

How does one write a test that fails only if a function doesn’t throw an expected exception?

Short Answer:

Use the self.assertRaises method as a context manager:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

Demonstration

The best practice approach is fairly easy to demonstrate in a Python shell.

The unittest library

In Python 2.7 or 3:

import unittest

In Python 2.6, you can install a backport of 2.7’s unittest library, called unittest2, and just alias that as unittest:

import unittest2 as unittest

Example tests

Now, paste into your Python shell the following test of Python’s type-safety:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

Test one uses assertRaises as a context manager, which ensures that the error is properly caught and cleaned up, while recorded.

We could also write it without the context manager, see test two. The first argument would be the error type you expect to raise, the second argument, the function you are testing, and the remaining args and keyword args will be passed to that function.

I think it’s far more simple, readable, and maintainable to just to use the context manager.

Running the tests

To run the tests:

unittest.main(exit=False)

In Python 2.6, you’ll probably need the following:

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

And your terminal should output the following:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

And we see that as we expect, attempting to add a 1 and a '1' result in a TypeError.


For more verbose output, try this:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

回答 4

您的代码应遵循以下模式(这是一个unittest模块样式测试):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

在Python <2.7上,此构造对于检查预期异常中的特定值很有用。unittest函数assertRaises仅检查是否引发了异常。

Your code should follow this pattern (this is a unittest module style test):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.


回答 5

来自:http : //www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

首先,这是文件dum_function.py中相应的(still dum:p)函数:

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

这是要执行的测试(仅插入此测试):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

   if __name__ == "__main__":
       unittest.main()

现在我们准备测试我们的功能!这是尝试运行测试时发生的情况:

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

TypeError被引发actullay,并生成测试失败。问题在于,这正是我们想要的行为:s。

为避免此错误,只需在测试调用中使用lambda运行该函数:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

最终输出:

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

太好了!

…对我来说也是完美的!

非常感谢Julien Lengrand-Lambert先生


这个测试断言实际上返回一个假阳性。发生这种情况是因为’assertRaises’内部的lambda是引发类型错误而不是经过测试的函数的单位。

from: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

First, here is the corresponding (still dum :p) function in file dum_function.py :

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

Here is the test to be performed (only this test is inserted):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

   if __name__ == "__main__":
       unittest.main()

We are now ready to test our function! Here is what happens when trying to run the test :

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

The TypeError is actullay raised, and generates a test failure. The problem is that this is exactly the behavior we wanted :s.

To avoid this error, simply run the function using lambda in the test call :

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

The final output :

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Perfect !

… and for me is perfect too!!

Thansk a lot Mr. Julien Lengrand-Lambert


This test assert actually returns a false positive. That happens because the lambda inside the ‘assertRaises’ is the unit that raises type error and not the tested function.


回答 6

您可以构建自己的程序contextmanager来检查是否引发了异常。

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

然后您可以raises像这样使用:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

如果您使用pytest,则该东西已经实现。您可以pytest.raises(Exception)

例:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

结果:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

You can build your own contextmanager to check if the exception was raised.

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

And then you can use raises like this:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

If you are using pytest, this thing is implemented already. You can do pytest.raises(Exception):

Example:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

And the result:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

回答 7

我几乎在所有地方都使用doctest [1],因为我喜欢同时记录和测试函数的事实。

看一下这段代码:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

如果将此示例放在模块中并从命令行运行它,则将评估并检查两个测试用例。

[1] Python文档:23.2 doctest-测试交互式Python示例

I use doctest[1] almost everywhere because I like the fact that I document and test my functions at the same time.

Have a look at this code:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

If you put this example in a module and run it from the command line both test cases are evaluated and checked.

[1] Python documentation: 23.2 doctest — Test interactive Python examples


回答 8

我刚刚发现,Mock库提供了assertRaisesWithMessage()方法(在其unittest.TestCase子类中),该方法不仅会检查是否引发了预期的异常,还检查了与预期消息一起引发的异常:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

I just discovered that the Mock library provides an assertRaisesWithMessage() method (in its unittest.TestCase subclass), which will check not only that the expected exception is raised, but also that it is raised with the expected message:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

回答 9

这里有很多答案。该代码显示了我们如何创建一个异常,如何在我们的方法中使用该异常,最后,您如何在单元测试中进行验证,并提出正确的异常。

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


if __name__ == '__main__':
    unittest.main()

There are a lot of answers here. The code shows how we can create an Exception, how we can use that exception in our methods, and finally, how you can verify in a unit test, the correct exceptions being raised.

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


if __name__ == '__main__':
    unittest.main()

回答 10

您可以使用unittest模块中的assertRaises

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

You can use assertRaises from the unittest module

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

回答 11

尽管所有答案都很好,但我仍在寻找一种方法来测试函数是否引发异常,而无需依赖于单元测试框架和编写测试类。

我最终写了以下内容:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

它在正确的行失败:

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError

While all the answers are perfectly fine, I was looking for a way to test if a function raised an exception without relying on unit testing frameworks and having to write test classes.

I ended up writing the following:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

And it fails on the right line :

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError

如何正确忽略异常

问题:如何正确忽略异常

当您只想执行try-except但不处理异常时,如何在Python中进行呢?

以下是正确的方法吗?

try:
    shutil.rmtree(path)
except:
    pass

When you just want to do a try-except without handling the exception, how do you do it in Python?

Is the following the right way to do it?

try:
    shutil.rmtree(path)
except:
    pass

回答 0

try:
    doSomething()
except: 
    pass

要么

try:
    doSomething()
except Exception: 
    pass

所不同的是,第一个也将赶上KeyboardInterruptSystemExit和类似的东西,这是直接来源于exceptions.BaseException,没有exceptions.Exception

有关详细信息,请参见文档:

try:
    doSomething()
except: 
    pass

or

try:
    doSomething()
except Exception: 
    pass

The difference is that the first one will also catch KeyboardInterrupt, SystemExit and stuff like that, which are derived directly from exceptions.BaseException, not exceptions.Exception.

See documentation for details:


回答 1

通常,仅捕获您感兴趣的错误是最佳实践。在这种情况下,shutil.rmtree可能是OSError

>>> shutil.rmtree("/fake/dir")
Traceback (most recent call last):
    [...]
OSError: [Errno 2] No such file or directory: '/fake/dir'

如果要静默忽略该错误,则可以执行以下操作:

try:
    shutil.rmtree(path)
except OSError:
    pass

为什么?说您(以某种方式)不小心将整数而不是字符串传递给函数,例如:

shutil.rmtree(2)

它将给出错误“ TypeError:强制转换为Unicode:需要字符串或缓冲区,找到int” -您可能不想忽略它,这可能很难调试。

如果您确实想忽略所有错误,请抓住Exception而不是仅仅except:声明。同样,为什么呢?

不指定异常会捕获所有异常,包括SystemExit例如sys.exit()使用的异常:

>>> try:
...     sys.exit(1)
... except:
...     pass
... 
>>>

将此与以下内容进行比较,即可正确退出:

>>> try:
...     sys.exit(1)
... except Exception:
...     pass
... 
shell:~$ 

如果您想编写更好的行为代码,则OSError异常可以表示各种错误,但是在上面的示例中,我们仅想忽略Errno 2,因此我们可以更加具体:

import errno

try:
    shutil.rmtree(path)
except OSError as e:
    if e.errno != errno.ENOENT:
        # ignore "No such file or directory", but re-raise other errors
        raise

It’s generally considered best-practice to only catch the errors you are interested in. In the case of shutil.rmtree it’s probably OSError:

>>> shutil.rmtree("/fake/dir")
Traceback (most recent call last):
    [...]
OSError: [Errno 2] No such file or directory: '/fake/dir'

If you want to silently ignore that error, you would do:

try:
    shutil.rmtree(path)
except OSError:
    pass

Why? Say you (somehow) accidently pass the function an integer instead of a string, like:

shutil.rmtree(2)

It will give the error “TypeError: coercing to Unicode: need string or buffer, int found” – you probably don’t want to ignore that, which can be difficult to debug.

If you definitely want to ignore all errors, catch Exception rather than a bare except: statement. Again, why?

Not specifying an exception catches every exception, including the SystemExit exception which for example sys.exit() uses:

>>> try:
...     sys.exit(1)
... except:
...     pass
... 
>>>

Compare this to the following, which correctly exits:

>>> try:
...     sys.exit(1)
... except Exception:
...     pass
... 
shell:~$ 

If you want to write ever better behaving code, the OSError exception can represent various errors, but in the example above we only want to ignore Errno 2, so we could be even more specific:

import errno

try:
    shutil.rmtree(path)
except OSError as e:
    if e.errno != errno.ENOENT:
        # ignore "No such file or directory", but re-raise other errors
        raise

回答 2

当您只想尝试捕获而不处理异常时,如何在Python中执行呢?

这取决于您所说的“处理”。

如果您打算不采取任何措施就将其捕获,则发布的代码将起作用。

如果您是想对异常采取措施而又不阻止异常上升,那么您需要这样的东西:

try:
    do_something()
except:
    handle_exception()
    raise  #re-raise the exact same exception that was thrown

When you just want to do a try catch without handling the exception, how do you do it in Python?

It depends on what you mean by “handling.”

If you mean to catch it without taking any action, the code you posted will work.

If you mean that you want to take action on an exception without stopping the exception from going up the stack, then you want something like this:

try:
    do_something()
except:
    handle_exception()
    raise  #re-raise the exact same exception that was thrown

回答 3

首先,我从这个话题中引述杰克·奥康纳的答案。引用的线程已关闭,所以我在这里写:

“ Python 3.4中引入了一种新的方法:

from contextlib import suppress

with suppress(Exception):
    # your code

这是添加了它的提交:http : //hg.python.org/cpython/rev/406b47c64480

这是作者Raymond Hettinger,讨论了这一点以及其他各种Python热度:https ://youtu.be/OSGv2VnC0go ? t = 43m23s

我对此的补充是Python 2.7等效项:

from contextlib import contextmanager

@contextmanager
def ignored(*exceptions):
    try:
        yield
    except exceptions:
        pass

然后像在Python 3.4中一样使用它:

with ignored(Exception):
    # your code

First I quote the answer of Jack o’Connor from this thread. The referenced thread got closed so I write here:

“There’s a new way to do this coming in Python 3.4:

from contextlib import suppress

with suppress(Exception):
    # your code

Here’s the commit that added it: http://hg.python.org/cpython/rev/406b47c64480

And here’s the author, Raymond Hettinger, talking about this and all sorts of other Python hotness: https://youtu.be/OSGv2VnC0go?t=43m23s

My addition to this is the Python 2.7 equivalent:

from contextlib import contextmanager

@contextmanager
def ignored(*exceptions):
    try:
        yield
    except exceptions:
        pass

Then you use it like in Python 3.4:

with ignored(Exception):
    # your code

回答 4

为了完整性:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")

还要注意,您可以像这样捕获异常:

>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print("Handling run-time error:", err)

…并重新引发如下异常:

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise

…来自python教程的示例。

For completeness:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")

Also note that you can capture the exception like this:

>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print("Handling run-time error:", err)

…and re-raise the exception like this:

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise

…examples from the python tutorial.


回答 5

如何正确忽略异常?

有几种方法可以做到这一点。

但是,示例的选择具有一个不包含一般情况的简单解决方案。

特定于示例:

代替

try:
    shutil.rmtree(path)
except:
    pass

做这个:

shutil.rmtree(path, ignore_errors=True)

这是特定于的论点shutil.rmtree。您可以通过执行以下操作来查看有关此操作的帮助,并且您还将看到它还允许错误处理功能。

>>> import shutil
>>> help(shutil.rmtree)

由于这仅涵盖了示例的狭义情况,因此我将进一步说明如果这些关键字参数不存在,该如何处理。

一般的做法

由于上面仅涵盖了示例的狭义情况,因此我将进一步演示如果这些关键字参数不存在,该如何处理。

Python 3.4的新功能:

您可以导入suppress上下文管理器:

from contextlib import suppress

但只禁止最具体的exceptions:

with suppress(FileNotFoundError):
    shutil.rmtree(path)

您将默默地忽略FileNotFoundError

>>> with suppress(FileNotFoundError):
...     shutil.rmtree('bajkjbkdlsjfljsf')
... 
>>> 

文档

与其他任何完全抑制异常的机制一样,此上下文管理器仅应用于涵盖非常具体的错误,在这些错误中,静默地继续执行程序是正确的做法。

请注意,suppress并且FileNotFoundError仅在Python 3中可用。

如果您还希望代码也可以在Python 2中运行,请参阅下一节:

Python 2和3:

当您只想尝试/exceptions而不处理异常时,如何在Python中进行呢?

以下是正确的方法吗?

try :
    shutil.rmtree ( path )
except :
    pass

对于与Python 2兼容的代码,这pass是不执行操作的正确方法。但是,当你做一个光秃秃的except:,这是一样的做except BaseException:,其中包括GeneratorExitKeyboardInterrupt,和SystemExit,一般来说,你不想要赶上那些东西。

实际上,在命名异常时应尽可能具体。

这是Python(2)异常层次结构的一部分,如您所见,如果您捕获了更多常规异常,则可以隐藏您没有想到的问题:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
... and so on

您可能想在这里捕获OSError,也许您不关心的异常是没有目录。

我们可以从库中获取特定的错误号errno,如果没有该错误号,则重新引发:

import errno

try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno == errno.ENOENT: # no such file or directory
        pass
    else: # we had an OSError we didn't expect, so reraise it
        raise 

请注意,不加薪将引发原始异常,在这种情况下,这可能就是您想要的。简明扼要,因为pass在异常处理中我们实际上不需要显式地使用代码:

try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno != errno.ENOENT: # no such file or directory
        raise 

How to properly ignore Exceptions?

There are several ways of doing this.

However, the choice of example has a simple solution that does not cover the general case.

Specific to the example:

Instead of

try:
    shutil.rmtree(path)
except:
    pass

Do this:

shutil.rmtree(path, ignore_errors=True)

This is an argument specific to shutil.rmtree. You can see the help on it by doing the following, and you’ll see it can also allow for functionality on errors as well.

>>> import shutil
>>> help(shutil.rmtree)

Since this only covers the narrow case of the example, I’ll further demonstrate how to handle this if those keyword arguments didn’t exist.

General approach

Since the above only covers the narrow case of the example, I’ll further demonstrate how to handle this if those keyword arguments didn’t exist.

New in Python 3.4:

You can import the suppress context manager:

from contextlib import suppress

But only suppress the most specific exception:

with suppress(FileNotFoundError):
    shutil.rmtree(path)

You will silently ignore a FileNotFoundError:

>>> with suppress(FileNotFoundError):
...     shutil.rmtree('bajkjbkdlsjfljsf')
... 
>>> 

From the docs:

As with any other mechanism that completely suppresses exceptions, this context manager should be used only to cover very specific errors where silently continuing with program execution is known to be the right thing to do.

Note that suppress and FileNotFoundError are only available in Python 3.

If you want your code to work in Python 2 as well, see the next section:

Python 2 & 3:

When you just want to do a try/except without handling the exception, how do you do it in Python?

Is the following the right way to do it?

try :
    shutil.rmtree ( path )
except :
    pass

For Python 2 compatible code, pass is the correct way to have a statement that’s a no-op. But when you do a bare except:, that’s the same as doing except BaseException: which includes GeneratorExit, KeyboardInterrupt, and SystemExit, and in general, you don’t want to catch those things.

In fact, you should be as specific in naming the exception as you can.

Here’s part of the Python (2) exception hierarchy, and as you can see, if you catch more general Exceptions, you can hide problems you did not expect:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
... and so on

You probably want to catch an OSError here, and maybe the exception you don’t care about is if there is no directory.

We can get that specific error number from the errno library, and reraise if we don’t have that:

import errno

try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno == errno.ENOENT: # no such file or directory
        pass
    else: # we had an OSError we didn't expect, so reraise it
        raise 

Note, a bare raise raises the original exception, which is probably what you want in this case. Written more concisely, as we don’t really need to explicitly pass with code in the exception handling:

try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno != errno.ENOENT: # no such file or directory
        raise 

回答 6

当您只想尝试捕获而不处理异常时,如何在Python中执行呢?

这将帮助您打印出异常是什么(例如,在不处理异常的情况下尝试捕获并打印异常。)

import sys
try:
    doSomething()
except:
    print "Unexpected error:", sys.exc_info()[0]

When you just want to do a try catch without handling the exception, how do you do it in Python?

This will help you to print what the exception is:( i.e. try catch without handling the exception and print the exception.)

import sys
try:
    doSomething()
except:
    print "Unexpected error:", sys.exc_info()[0]

回答 7

try:
      doSomething()
except Exception: 
    pass
else:
      stuffDoneIf()
      TryClauseSucceeds()

仅供参考,else子句可以在所有异常之后执行,并且仅在try中的代码不会引起异常的情况下才会运行。

try:
      doSomething()
except Exception: 
    pass
else:
      stuffDoneIf()
      TryClauseSucceeds()

FYI the else clause can go after all exceptions and will only be run if the code in the try doesn’t cause an exception.


回答 8

我需要忽略多个命令中的错误,fuckit做到了

import fuckit

@fuckit
def helper():
    print('before')
    1/0
    print('after1')
    1/0
    print('after2')

helper()

I needed to ignore errors in multiple commands and fuckit did the trick

import fuckit

@fuckit
def helper():
    print('before')
    1/0
    print('after1')
    1/0
    print('after2')

helper()

回答 9

在Python中,我们处理与其他语言相似的异常,但是区别在于语法上有些差异,例如,

try:
    #Your code in which exception can occur
except <here we can put in a particular exception name>:
    # We can call that exception here also, like ZeroDivisionError()
    # now your code
# We can put in a finally block also
finally:
    # Your code...

In Python, we handle exceptions similar to other language, but the difference is some syntax difference, for example,

try:
    #Your code in which exception can occur
except <here we can put in a particular exception name>:
    # We can call that exception here also, like ZeroDivisionError()
    # now your code
# We can put in a finally block also
finally:
    # Your code...

回答 10

我通常只是这样做:

try:
    doSomething()
except:
    _ = ""

I usually just do:

try:
    doSomething()
except:
    _ = ""

如何在Python中打印异常?

问题:如何在Python中打印异常?

try:
    something here
except:
    print('the whatever error occurred.')

如何在except:块中打印错误/异常?

try:
    something here
except:
    print('the whatever error occurred.')

How can I print the error/exception in my except: block?


回答 0

对于Python 2.6和更高版本以及Python 3.x:

except Exception as e: print(e)

对于Python 2.5及更早版本,请使用:

except Exception,e: print str(e)

For Python 2.6 and later and Python 3.x:

except Exception as e: print(e)

For Python 2.5 and earlier, use:

except Exception,e: print str(e)

回答 1

traceback模块提供了格式化和打印异常及其回溯的方法,例如,它将像默认处理程序那样打印异常:

import traceback

try:
    1/0
except Exception:
    traceback.print_exc()

输出:

Traceback (most recent call last):
  File "C:\scripts\divide_by_zero.py", line 4, in <module>
    1/0
ZeroDivisionError: division by zero

The traceback module provides methods for formatting and printing exceptions and their tracebacks, e.g. this would print exception like the default handler does:

import traceback

try:
    1/0
except Exception:
    traceback.print_exc()

Output:

Traceback (most recent call last):
  File "C:\scripts\divide_by_zero.py", line 4, in <module>
    1/0
ZeroDivisionError: division by zero

回答 2

Python 2.6或更高版本中,它更干净一些:

except Exception as e: print(e)

在旧版本中,它仍然很可读:

except Exception, e: print e

In Python 2.6 or greater it’s a bit cleaner:

except Exception as e: print(e)

In older versions it’s still quite readable:

except Exception, e: print e

回答 3

如果您想传递错误字符串,这是错误和异常(Python 2.6)中的示例

>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print type(inst)     # the exception instance
...    print inst.args      # arguments stored in .args
...    print inst           # __str__ allows args to printed directly
...    x, y = inst          # __getitem__ allows args to be unpacked directly
...    print 'x =', x
...    print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

In case you want to pass error strings, here is an example from Errors and Exceptions (Python 2.6)

>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print type(inst)     # the exception instance
...    print inst.args      # arguments stored in .args
...    print inst           # __str__ allows args to printed directly
...    x, y = inst          # __getitem__ allows args to be unpacked directly
...    print 'x =', x
...    print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

回答 4

(我打算将其作为对@jldupont答案的评论,但我没有足够的声誉。)

我在其他地方也看到过类似@jldupont的答案的答案。FWIW,我认为必须注意以下几点:

except Exception as e:
    print(e)

sys.stdout默认将错误输出打印到。通常,更合适的错误处理方法是:

except Exception as e:
    print(e, file=sys.stderr)

(请注意,您必须import sys执行此操作。)这样,将错误打印到STDERR而不是STDOUT,从而可以进行正确的输出解析/重定向/等。我知道问题完全是关于“打印错误”的,但是在此处指出最佳实践而不是忽略可能导致最终学习不到的标准代码的细节似乎很重要。

我没有traceback在Cat Plus Plus的答案中使用该模块,也许这是最好的方法,但是我想我应该把它扔在那里。

(I was going to leave this as a comment on @jldupont’s answer, but I don’t have enough reputation.)

I’ve seen answers like @jldupont’s answer in other places as well. FWIW, I think it’s important to note that this:

except Exception as e:
    print(e)

will print the error output to sys.stdout by default. A more appropriate approach to error handling in general would be:

except Exception as e:
    print(e, file=sys.stderr)

(Note that you have to import sys for this to work.) This way, the error is printed to STDERR instead of STDOUT, which allows for the proper output parsing/redirection/etc. I understand that the question was strictly about ‘printing an error’, but it seems important to point out the best practice here rather than leave out this detail that could lead to non-standard code for anyone who doesn’t eventually learn better.

I haven’t used the traceback module as in Cat Plus Plus’s answer, and maybe that’s the best way, but I thought I’d throw this out there.


回答 5

Python 3: logging

除了使用基本print()功能,logging还可以使用更灵活的模块来记录异常。该logging模块提供了许多额外的功能,例如,将消息记录到给定的日志文件中,使用时间戳记录消息以及有关记录发生位置的其他信息。(有关更多信息,请查看官方文档。)

可以使用模块级功能记录异常,logging.exception()如下所示:

import logging

try:
    1/0
except BaseException:
    logging.exception("An exception was thrown!")

输出:

ERROR:root:An exception was thrown!
Traceback (most recent call last):
  File ".../Desktop/test.py", line 4, in <module>
    1/0
ZeroDivisionError: division by zero 

笔记:

  • 该功能logging.exception()只能从异常处理程序中调用

  • logging模块不应在日志记录处理程序中使用,以免出现RecursionError(感谢@PrakharPandey)


备用日志级别

也可以使用关键字参数将异常记录到另一个日志级别,exc_info=True如下所示:

logging.debug("An exception was thrown!", exc_info=True)
logging.info("An exception was thrown!", exc_info=True)
logging.warning("An exception was thrown!", exc_info=True)

Python 3: logging

Instead of using the basic print() function, the more flexible logging module can be used to log the exception. The logging module offers a lot extra functionality, e.g. logging messages into a given log file, logging messages with timestamps and additional information about where the logging happened. (For more information check out the official documentation.)

Logging an exception can be done with the module-level function logging.exception() like so:

import logging

try:
    1/0
except BaseException:
    logging.exception("An exception was thrown!")

Output:

ERROR:root:An exception was thrown!
Traceback (most recent call last):
  File ".../Desktop/test.py", line 4, in <module>
    1/0
ZeroDivisionError: division by zero 

Notes:

  • the function logging.exception() should only be called from an exception handler

  • the logging module should not be used inside a logging handler to avoid a RecursionError (thanks @PrakharPandey)


Alternative log-levels

It’s also possible to log the exception with another log-level by using the keyword argument exc_info=True like so:

logging.debug("An exception was thrown!", exc_info=True)
logging.info("An exception was thrown!", exc_info=True)
logging.warning("An exception was thrown!", exc_info=True)

回答 6

如果您要这样做,可以使用assert语句来完成一次线性错误提升。这将帮助您编写可静态修复的代码并及早检查错误。

assert type(A) is type(""), "requires a string"

One liner error raising can be done with assert statements if that’s what you want to do. This will help you write statically fixable code and check errors early.

assert type(A) is type(""), "requires a string"

回答 7

在捕获异常时,几乎可以控制要显示/记录的追溯信息。

编码

with open("not_existing_file.txt", 'r') as text:
    pass

将产生以下回溯:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

打印/记录完整的追溯

正如其他人已经提到的那样,您可以使用traceback模块捕获整个traceback:

import traceback
try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    traceback.print_exc()

这将产生以下输出:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

您可以通过使用日志记录来实现相同目的:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    logger.error(exception, exc_info=True)

输出:

__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
  File "exception_checks.py", line 27, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

仅打印/记录错误名称/消息

您可能对整个追溯不感兴趣,而仅对最重要的信息(例如,异常名称和异常消息)感兴趣,请使用:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    print("Exception: {}".format(type(exception).__name__))
    print("Exception message: {}".format(exception))

输出:

Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'

One has pretty much control on which information from the traceback to be displayed/logged when catching exceptions.

The code

with open("not_existing_file.txt", 'r') as text:
    pass

would produce the following traceback:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/Log the full traceback

As others already mentioned, you can catch the whole traceback by using the traceback module:

import traceback
try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    traceback.print_exc()

This will produce the following output:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

You can achieve the same by using logging:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    logger.error(exception, exc_info=True)

Output:

__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
  File "exception_checks.py", line 27, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/log error name/message only

You might not be interested in the whole traceback, but only in the most important information, such as Exception name and Exception message, use:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    print("Exception: {}".format(type(exception).__name__))
    print("Exception message: {}".format(exception))

Output:

Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'

在Python中手动引发(抛出)异常

问题:在Python中手动引发(抛出)异常

如何在Python中引发异常,以便以后可以通过except块将其捕获?

How can I raise an exception in Python so that it can later be caught via an except block?


回答 0

如何在Python中手动引发/引发异常?

使用在语义上适合您的问题的最特定的Exception构造函数

在您的消息中要具体,例如:

raise ValueError('A very specific bad thing happened.')

不要引发通用异常

避免提出泛型Exception。要捕获它,您必须捕获将其子类化的所有其他更具体的异常。

问题1:隐藏错误

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

例如:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

问题2:无法抓住

而且更具体的捕获不会捕获一般异常:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

最佳做法:raise声明

而是使用在语义上适合您的issue的最特定的Exception构造函数

raise ValueError('A very specific bad thing happened')

这也方便地允许将任意数量的参数传递给构造函数:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

这些参数由对象args上的属性访问Exception。例如:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

版画

('message', 'foo', 'bar', 'baz')    

在Python 2.5中,message添加了一个实际属性,以BaseException鼓励用户继承Exceptions子类并停止使用args,但是args 的引入message和最初的弃用已被收回

最佳做法:except条款

例如,在except子句中时,您可能想要记录发生了特定类型的错误,然后重新引发。保留堆栈跟踪时执行此操作的最佳方法是使用裸机抬高语句。例如:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

不要修改您的错误…但是如果您坚持的话。

您可以使用来保留stacktrace(和错误值)sys.exc_info(),但这更容易出错,并且在Python 2和3之间存在兼容性问题,建议使用裸机raise重新引发。

解释- sys.exc_info()返回类型,值和回溯。

type, value, traceback = sys.exc_info()

这是Python 2中的语法-请注意,这与Python 3不兼容:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

如果愿意,您可以修改新加薪后的情况-例如args,为实例设置新值:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

并且我们在修改args时保留了整个回溯。请注意,这不是最佳做法,并且在Python 3中是无效的语法(使得保持兼容性变得更加困难)。

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

Python 3中

    raise error.with_traceback(sys.exc_info()[2])

同样:避免手动操作回溯。它效率较低,更容易出错。而且,如果您正在使用线程,sys.exc_info甚至可能会得到错误的回溯(特别是如果您对控制流使用异常处理,我个人倾向于避免这种情况。)

Python 3,异常链接

在Python 3中,您可以链接异常,以保留回溯:

    raise RuntimeError('specific message') from error

意识到:

  • 确实允许更改引发的错误类型,并且
  • 这与Python 2 兼容。

不推荐使用的方法:

这些可以轻松隐藏甚至进入生产代码。您想提出一个exceptions,而这样做会引发一个exceptions,但不是一个预期的exceptions

在Python 2中有效,但在Python 3中无效

raise ValueError, 'message' # Don't do this, it's deprecated!

在更旧的Python版本(2.4及更低版本)中有效,您仍然可以看到有人在引发字符串:

raise 'message' # really really wrong. don't do this.

在所有现代版本中,这实际上会引发一个TypeError,因为您没有引发一个BaseException类型。如果您没有检查正确的exceptions情况,并且没有知道此问题的审阅者,那么它可能会投入生产。

用法示例

我提出异常以警告使用者如果我的API使用不正确:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

适当时创建自己的错误类型

“我想故意犯一个错误,以便将其排除在外”

您可以创建自己的错误类型,如果要指示应用程序存在某些特定的错误,只需在异常层次结构中将适当的点子类化:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

和用法:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

How do I manually throw/raise an exception in Python?

Use the most specific Exception constructor that semantically fits your issue.

Be specific in your message, e.g.:

raise ValueError('A very specific bad thing happened.')

Don’t raise generic exceptions

Avoid raising a generic Exception. To catch it, you’ll have to catch all other more specific exceptions that subclass it.

Problem 1: Hiding bugs

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

For example:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problem 2: Won’t catch

And more specific catches won’t catch the general exception:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Best Practices: raise statement

Instead, use the most specific Exception constructor that semantically fits your issue.

raise ValueError('A very specific bad thing happened')

which also handily allows an arbitrary number of arguments to be passed to the constructor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

These arguments are accessed by the args attribute on the Exception object. For example:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

prints

('message', 'foo', 'bar', 'baz')    

In Python 2.5, an actual message attribute was added to BaseException in favor of encouraging users to subclass Exceptions and stop using args, but the introduction of message and the original deprecation of args has been retracted.

Best Practices: except clause

When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. The best way to do this while preserving the stack trace is to use a bare raise statement. For example:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Don’t modify your errors… but if you insist.

You can preserve the stacktrace (and error value) with sys.exc_info(), but this is way more error prone and has compatibility problems between Python 2 and 3, prefer to use a bare raise to re-raise.

To explain – the sys.exc_info() returns the type, value, and traceback.

type, value, traceback = sys.exc_info()

This is the syntax in Python 2 – note this is not compatible with Python 3:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

If you want to, you can modify what happens with your new raise – e.g. setting new args for the instance:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

And we have preserved the whole traceback while modifying the args. Note that this is not a best practice and it is invalid syntax in Python 3 (making keeping compatibility much harder to work around).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

In Python 3:

    raise error.with_traceback(sys.exc_info()[2])

Again: avoid manually manipulating tracebacks. It’s less efficient and more error prone. And if you’re using threading and sys.exc_info you may even get the wrong traceback (especially if you’re using exception handling for control flow – which I’d personally tend to avoid.)

Python 3, Exception chaining

In Python 3, you can chain Exceptions, which preserve tracebacks:

    raise RuntimeError('specific message') from error

Be aware:

  • this does allow changing the error type raised, and
  • this is not compatible with Python 2.

Deprecated Methods:

These can easily hide and even get into production code. You want to raise an exception, and doing them will raise an exception, but not the one intended!

Valid in Python 2, but not in Python 3 is the following:

raise ValueError, 'message' # Don't do this, it's deprecated!

Only valid in much older versions of Python (2.4 and lower), you may still see people raising strings:

raise 'message' # really really wrong. don't do this.

In all modern versions, this will actually raise a TypeError, because you’re not raising a BaseException type. If you’re not checking for the right exception and don’t have a reviewer that’s aware of the issue, it could get into production.

Example Usage

I raise Exceptions to warn consumers of my API if they’re using it incorrectly:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Create your own error types when apropos

“I want to make an error on purpose, so that it would go into the except”

You can create your own error types, if you want to indicate something specific is wrong with your application, just subclass the appropriate point in the exception hierarchy:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

and usage:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

回答 1

不要这样做。赤身裸体Exception绝对不是正确的选择。请参阅亚伦·霍尔(Aaron Hall)的出色答案

不能得到比这更多的pythonic:

raise Exception("I know python!")

如果您需要更多信息,请参阅python 的凸起语句文档

DON’T DO THIS. Raising a bare Exception is absolutely not the right thing to do; see Aaron Hall’s excellent answer instead.

Can’t get much more pythonic than this:

raise Exception("I know python!")

See the raise statement docs for python if you’d like more info.


回答 2

在Python3中,有四种用于引发异常的语法:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1.引发异常vs. 2.引发异常(参数)

如果raise exception (args) 用于引发异常,则在 args打印异常对象时将打印出-如下例所示。

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.提高

raise不带任何参数的语句重新引发最后一个异常。如果您需要在捕获异常后执行一些操作然后重新引发它,这将很有用。但是,如果以前没有异常,则raise语句引发 TypeErrorException。

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4.从original_exception引发异常(参数)

该语句用于创建异常链接,其中响应另一个异常而引发的异常可以包含原始异常的详细信息-如下例所示。

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

输出:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

In Python3 there are 4 different syntaxes for rasing exceptions:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. raise exception vs. 2. raise exception (args)

If you use raise exception (args) to raise an exception then the args will be printed when you print the exception object – as shown in the example below.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raise statement without any arguments re-raises the last exception. This is useful if you need to perform some actions after catching the exception and then want to re-raise it. But if there was no exception before, raise statement raises TypeError Exception.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. raise exception (args) from original_exception

This statement is used to create exception chaining in which an exception that is raised in response to another exception can contain the details of the original exception – as shown in the example below.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Output:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

回答 3

对于常见的情况,您需要针对某些意外情况抛出异常,并且您从不打算抓住它,而只是快速失败以使您能够从那里进行调试(如果发生的话)—最合乎逻辑的是AssertionError

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

For the common case where you need to throw an exception in response to some unexpected conditions, and that you never intend to catch, but simply to fail fast to enable you to debug from there if it ever happens — the most logical one seems to be AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

回答 4

首先阅读现有的答案,这只是一个附录。

请注意,可以带或不带参数引发异常。

例:

raise SystemExit

退出程序,但是您可能想知道发生了什么。因此可以使用它。

raise SystemExit("program exited")

这将在关闭程序之前将“程序退出”打印到stderr。

Read the existing answers first, this is just an addendum.

Notice that you can raise exceptions with or without arguments.

Example:

raise SystemExit

exits the program but you might want to know what happened.So you can use this.

raise SystemExit("program exited")

this will print “program exited” to stderr before closing the program.


回答 5

抛出异常的另一种方法是assert。您可以使用assert来验证是否满足条件,否则条件将上升AssertionError。有关更多详细信息,请在此处查看

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

Another way to throw an exceptions is assert. You can use assert to verify a condition is being fulfilled if not then it will raise AssertionError. For more details have a look here.

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

回答 6

只是要注意:有时候您确实想处理通用异常。如果要处理大量文件并记录错误,则可能要捕获文件发生的任何错误,将其记录下来,然后继续处理其余文件。在这种情况下,

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

阻止这样做的好方法。您仍然需要raise特定的异常,以便您了解异常的含义。

Just to note: there are times when you DO want to handle generic exceptions. If you’re processing a bunch of files and logging your errors, you might want to catch any error that occurs for a file, log it, and continue processing the rest of the files. In that case, a

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

block a good way to do it. You’ll still want to raise specific exceptions so you know what they mean, though.


回答 7

您应该为此学习python的凸起语句。它应该保存在try块中。范例-

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')

You should learn the raise statement of python for that. It should be kept inside the try block. Example –

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')

在现代Python中声明自定义异常的正确方法?

问题:在现代Python中声明自定义异常的正确方法?

在现代Python中声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类具有的任何标准,以便(例如)我捕获到异常中的任何工具都会打印出我包含在异常中的任何多余字符串。

“现代Python”是指可以在Python 2.5中运行但对于Python 2.6和Python 3. *是“正确”的方式。所谓“自定义”,是指一个Exception对象,该对象可以包含有关错误原因的其他数据:字符串,也可以是与该异常相关的其他任意对象。

我在Python 2.6.2中被以下弃用警告绊倒了:

>>> class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
... 
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

BaseException对于名为的属性有特殊含义似乎很疯狂message。我从PEP-352收集到,该属性确实在2.5中有特殊含义,因此他们想弃用该属性,所以我想现在禁止使用该名称了(并且一个人)。啊。

我也模糊地意识到它Exception具有一些不可思议的参数args,但是我从未知道如何使用它。我也不确定这是前进的正确方法。我在网上发现的很多讨论都表明他们正在尝试消除Python 3中的args。

更新:有两个答案建议覆盖__init__,和__str__/ __unicode__/ __repr__。好像要打字很多,有必要吗?

What’s the proper way to declare custom exception classes in modern Python? My primary goal is to follow whatever standard other exception classes have, so that (for instance) any extra string I include in the exception is printed out by whatever tool caught the exception.

By “modern Python” I mean something that will run in Python 2.5 but be ‘correct’ for the Python 2.6 and Python 3.* way of doing things. And by “custom” I mean an Exception object that can include extra data about the cause of the error: a string, maybe also some other arbitrary object relevant to the exception.

I was tripped up by the following deprecation warning in Python 2.6.2:

>>> class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
... 
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

It seems crazy that BaseException has a special meaning for attributes named message. I gather from PEP-352 that attribute did have a special meaning in 2.5 they’re trying to deprecate away, so I guess that name (and that one alone) is now forbidden? Ugh.

I’m also fuzzily aware that Exception has some magic parameter args, but I’ve never known how to use it. Nor am I sure it’s the right way to do things going forward; a lot of the discussion I found online suggested they were trying to do away with args in Python 3.

Update: two answers have suggested overriding __init__, and __str__/__unicode__/__repr__. That seems like a lot of typing, is it necessary?


回答 0

也许我错过了这个问题,但是为什么不呢?

class MyException(Exception):
    pass

编辑:要覆盖某些内容(或传递额外的args),请执行以下操作:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

这样,您可以将错误消息的字典传递给第二个参数,并在以后使用 e.errors


Python 3更新:在Python 3+中,您可以使用以下更紧凑的用法super()

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super().__init__(message)

        # Now for your custom code...
        self.errors = errors

Maybe I missed the question, but why not:

class MyException(Exception):
    pass

Edit: to override something (or pass extra args), do this:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

That way you could pass dict of error messages to the second param, and get to it later with e.errors


Python 3 Update: In Python 3+, you can use this slightly more compact use of super():

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super().__init__(message)

        # Now for your custom code...
        self.errors = errors

回答 1

随着现代Python的exceptions,你并不需要滥用.message,或覆盖.__str__().__repr__()或任何它。如果在引发异常时,您所希望的只是一条提示性消​​息,请执行以下操作:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

这将提供以结尾的回溯MyException: My hovercraft is full of eels

如果您希望从异常中获得更大的灵活性,则可以传递一个字典作为参数:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

但是,要在一个except块中获得这些详细信息则要复杂一些。详细信息存储在args列表中的属性中。您将需要执行以下操作:

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

仍然可以将多个项目传递给异常并通过元组索引访问它们,但是强烈建议不要这样做(甚至是打算在不久后弃用)。如果确实需要多个信息,而上述方法不足以满足您的要求,则应Exception按照本教程中的描述进行子类化。

class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message

With modern Python Exceptions, you don’t need to abuse .message, or override .__str__() or .__repr__() or any of it. If all you want is an informative message when your exception is raised, do this:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

That will give a traceback ending with MyException: My hovercraft is full of eels.

If you want more flexibility from the exception, you could pass a dictionary as the argument:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

However, to get at those details in an except block is a bit more complicated. The details are stored in the args attribute, which is a list. You would need to do something like this:

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

It is still possible to pass in multiple items to the exception and access them via tuple indexes, but this is highly discouraged (and was even intended for deprecation a while back). If you do need more than a single piece of information and the above method is not sufficient for you, then you should subclass Exception as described in the tutorial.

class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message

回答 2

“在现代Python中声明自定义异常的正确方法?”

很好,除非您的异常确实是更具体的异常的一种:

class MyException(Exception):
    pass

或者更好(也许是完美的),而不是pass提供一个文档字符串:

class MyException(Exception):
    """Raise for my specific kind of exception"""

子类化异常子类

来自文档

Exception

所有内置的,非系统退出的异常都派生自此类。所有用户定义的异常也应从此类派生。

这意味着,如果您的异常是一种更具体的异常,请将该异常归为子类,而不是泛型Exception(其结果将是您仍然Exception按照文档建议的方式派生)。另外,您至少可以提供一个文档字符串(并且不被迫使用pass关键字):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

设置可使用custom创建自己的属性__init__。避免将dict作为位置参数传递,以后您的代码用户将感谢您。如果您使用不推荐使用的message属性,则自行分配该属性将避免出现DeprecationWarning

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

确实不需要自己编写__str____repr__。内置的非常好,您的合作继承可确保您使用它。

批判最佳答案

也许我错过了这个问题,但是为什么不呢?

class MyException(Exception):
    pass

同样,上面的问题是,要捕获它,您必须专门为其命名(如果在其他位置创建,则将其导入)或捕获Exception(但您可能不准备处理所有类型的Exception,并且您应该只捕获您准备处理的异常)。与以下内容类似的批评,但除此之外,这不是通过进行初始化的方式superDeprecationWarning如果您访问message属性,则会得到一个:

编辑:要覆盖某些内容(或传递额外的args),请执行以下操作:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

这样,您可以将错误消息的字典传递给第二个参数,并在以后使用e.errors到达它。

它也需要传入两个参数(self。除外)。这是一个有趣的约束,将来的用户可能不会欣赏。

直截了当-它违反了Liskov的可替代性

我将演示两个错误:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

相比:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

“Proper way to declare custom exceptions in modern Python?”

This is fine, unless your exception is really a type of a more specific exception:

class MyException(Exception):
    pass

Or better (maybe perfect), instead of pass give a docstring:

class MyException(Exception):
    """Raise for my specific kind of exception"""

Subclassing Exception Subclasses

From the docs

Exception

All built-in, non-system-exiting exceptions are derived from this class. All user-defined exceptions should also be derived from this class.

That means that if your exception is a type of a more specific exception, subclass that exception instead of the generic Exception (and the result will be that you still derive from Exception as the docs recommend). Also, you can at least provide a docstring (and not be forced to use the pass keyword):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

Set attributes you create yourself with a custom __init__. Avoid passing a dict as a positional argument, future users of your code will thank you. If you use the deprecated message attribute, assigning it yourself will avoid a DeprecationWarning:

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

There’s really no need to write your own __str__ or __repr__. The builtin ones are very nice, and your cooperative inheritance ensures that you use it.

Critique of the top answer

Maybe I missed the question, but why not:

class MyException(Exception):
    pass

Again, the problem with the above is that in order to catch it, you’ll either have to name it specifically (importing it if created elsewhere) or catch Exception, (but you’re probably not prepared to handle all types of Exceptions, and you should only catch exceptions you are prepared to handle). Similar criticism to the below, but additionally that’s not the way to initialize via super, and you’ll get a DeprecationWarning if you access the message attribute:

Edit: to override something (or pass extra args), do this:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

That way you could pass dict of error messages to the second param, and get to it later with e.errors

It also requires exactly two arguments to be passed in (aside from the self.) No more, no less. That’s an interesting constraint that future users may not appreciate.

To be direct – it violates Liskov substitutability.

I’ll demonstrate both errors:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

Compared to:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

回答 3

见异常缺省情况下是如何工作的,如果一个VS多个属性使用(回溯略):

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

因此,您可能需要一种“ 异常模板 ”,以兼容的方式作为异常本身工作:

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

使用此子类可以轻松完成此操作

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

如果您不喜欢默认的类似元组的表示形式,只需将__str__方法添加到ExceptionTemplate类中,例如:

    # ...
    def __str__(self):
        return ': '.join(self.args)

然后你会

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken

see how exceptions work by default if one vs more attributes are used (tracebacks omitted):

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

so you might want to have a sort of “exception template“, working as an exception itself, in a compatible way:

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

this can be done easily with this subclass

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

and if you don’t like that default tuple-like representation, just add __str__ method to the ExceptionTemplate class, like:

    # ...
    def __str__(self):
        return ': '.join(self.args)

and you’ll have

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken

回答 4

从Python 3.8(2018,https://docs.python.org/dev/whatsnew/3.8.html)开始,推荐的方法仍然是:

class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass

请不要忘记记录文档,为什么需要自定义异常!

如果需要,这是处理包含更多数据的异常的方法:

class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types

并像这样获取它们:

try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1

payload=None使它变得可腌很重要。转储之前,您必须调用error.__reduce__()。加载将按预期工作。

return如果您需要将大量数据传输到某些外部结构,则可能应该调查使用pythons 语句查找解决方案。对我来说,这似乎更清楚/更pythonic。高级异常在Java中大量使用,当使用框架并不得不捕获所有可能的错误时,有时会很烦人。

As of Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html), the recommended method is still:

class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass

Please don’t forget to document, why a custom exception is neccessary!

If you need to, this is the way to go for exceptions with more data:

class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types

and fetch them like:

try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1

payload=None is important to make it pickle-able. Before dumping it, you have to call error.__reduce__(). Loading will work as expected.

You maybe should investigate in finding a solution using pythons return statement if you need much data to be transferred to some outer structure. This seems to be clearer/more pythonic to me. Advanced exceptions are heavily used in Java, which can sometimes be annoying, when using a framework and having to catch all possible errors.


回答 5

您应该重写__repr____unicode__方法,而不使用消息,构造异常时提供的参数将位于args异常对象的属性中。

You should override __repr__ or __unicode__ methods instead of using message, the args you provide when you construct the exception will be in the args attribute of the exception object.


回答 6

不,“消息”不是禁止的。只是过时了。您的应用程序可以正常使用消息。但是,您当然可以摆脱折旧错误。

当为应用程序创建自定义Exception类时,它们中的许多不仅仅从Exception继承子类,还从ValueError之类的其他子类继承子类。然后,您必须适应它们对变量的使用。

而且,如果您的应用程序中有很多异常,通常最好为所有异常都拥有一个通用的自定义基类,以便模块的用户可以

try:
    ...
except NelsonsExceptions:
    ...

在这种情况下,您可以在__init__ and __str__那里进行所需的操作,因此您不必为每个异常重复执行该操作。但是简单地调用message变量而不是message可以解决问题。

无论如何,__init__ or __str__如果您做的事情与Exception本身不同,则仅需要使用。并且因为如果不赞成使用,那么您将同时需要两者,否则您将得到一个错误。每个类不需要很多额外的代码。;)

No, “message” is not forbidden. It’s just deprecated. You application will work fine with using message. But you may want to get rid of the deprecation error, of course.

When you create custom Exception classes for your application, many of them do not subclass just from Exception, but from others, like ValueError or similar. Then you have to adapt to their usage of variables.

And if you have many exceptions in your application it’s usually a good idea to have a common custom base class for all of them, so that users of your modules can do

try:
    ...
except NelsonsExceptions:
    ...

And in that case you can do the __init__ and __str__ needed there, so you don’t have to repeat it for every exception. But simply calling the message variable something else than message does the trick.

In any case, you only need the __init__ or __str__ if you do something different from what Exception itself does. And because if the deprecation, you then need both, or you get an error. That’s not a whole lot of extra code you need per class. ;)


回答 7

请参阅非常好的文章“ Python异常的权威指南 ”。基本原则是:

  • 始终从(至少)继承。
  • 始终BaseException.__init__仅使用一个参数进行调用。
  • 构建库时,请定义从Exception继承的基类。
  • 提供有关错误的详细信息。
  • 从内置异常类型继承是有意义的。

还有关于组织(在模块中)和包装异常的信息,我建议阅读指南。

See a very good article “The definitive guide to Python exceptions“. The basic principles are:

  • Always inherit from (at least) Exception.
  • Always call BaseException.__init__ with only one argument.
  • When building a library, define a base class inheriting from Exception.
  • Provide details about the error.
  • Inherit from builtin exceptions types when it makes sense.

There is also information on organizing (in modules) and wrapping exceptions, I recommend to read the guide.


回答 8

试试这个例子

class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")

Try this Example

class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")

回答 9

要正确定义自己的异常,需要遵循一些最佳实践:

  • 定义一个继承自的基类Exception。这将允许捕获与项目相关的任何异常(更具体的异常应从该项目继承):

    class MyProjectError(Exception):
        """A base class for MyProject exceptions."""

    在单独的模块(例如exceptions.py)中组织这些异常类通常是一个好主意。

  • 要将额外的参数传递给您的异常,请定义一个__init__()具有可变数量参数的自定义方法。调用基类__init__()将任何位置参数传递给它(记住BaseException/Exception期望有任意数量的位置参数):

    class CustomError(MyProjectError):
        def __init__(self, *args, **kwargs):
            super(CustomError, self).__init__(*args)
            self.foo = kwargs.get('foo')

    要使用额外的参数引发此类异常,可以使用:

    raise CustomError('Something bad happened', foo='foo')

    这样的设计居然坚持里氏替换原则,因为你可以用一个派生的异常类的实例代替基本异常类的实例。

To define your own exceptions correctly, there are a few best practices that you need to follow:

  • Define a base class inheriting from Exception. This will allow to catch any exception related to the project (more specific exceptions should inherit from it):

    class MyProjectError(Exception):
        """A base class for MyProject exceptions."""
    

    Organizing these exception classes in a separate module (e.g. exceptions.py) is generally a good idea.

  • To pass extra argument(s) to your exception, define a custom __init__() method with a variable number of arguments. Call the base class’s __init__() passing any positional arguments to it (remember that BaseException/Exception expect any number of positional arguments):

    class CustomError(MyProjectError):
        def __init__(self, *args, **kwargs):
            super(CustomError, self).__init__(*args)
            self.foo = kwargs.get('foo')
    

    To raise such exception with an extra argument you can use:

    raise CustomError('Something bad happened', foo='foo')
    

    This design actually adheres to the Liskov substitution principle, since you can replace an instance of a base exception class with an instance of a derived exception class.


在一行中捕获多个异常(块除外)

问题:在一行中捕获多个异常(块除外)

我知道我可以做到:

try:
    # do something that may fail
except:
    # do this if ANYTHING goes wrong

我也可以这样做:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreTooShortException:
    # stand on a ladder

但是,如果我想在两个不同的异常中做同样的事情,那么我现在想到的最好的方法就是:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreBeingMeanException:
    # say please

有什么办法可以做这样的事情(因为在两种情况下都要采取的行动是say please):

try:
    # do something that may fail
except IDontLikeYouException, YouAreBeingMeanException:
    # say please

现在,这确实不起作用,因为它与以下语法匹配:

try:
    # do something that may fail
except Exception, e:
    # say please

因此,我捕捉两个截然不同的异常的努力并未完全实现。

有没有办法做到这一点?

I know that I can do:

try:
    # do something that may fail
except:
    # do this if ANYTHING goes wrong

I can also do this:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreTooShortException:
    # stand on a ladder

But if I want to do the same thing inside two different exceptions, the best I can think of right now is to do this:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreBeingMeanException:
    # say please

Is there any way that I can do something like this (since the action to take in both exceptions is to say please):

try:
    # do something that may fail
except IDontLikeYouException, YouAreBeingMeanException:
    # say please

Now this really won’t work, as it matches the syntax for:

try:
    # do something that may fail
except Exception, e:
    # say please

So, my effort to catch the two distinct exceptions doesn’t exactly come through.

Is there a way to do this?


回答 0

Python文档

例如,except子句可以将多个异常命名为带括号的元组。

except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

或者,仅对于Python 2:

except (IDontLikeYouException, YouAreBeingMeanException), e:
    pass

用逗号将变量与变量分开仍然可以在Python 2.6和2.7中使用,但是现在已弃用,并且在Python 3中不起作用。现在您应该使用as

From Python Documentation:

An except clause may name multiple exceptions as a parenthesized tuple, for example

except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

Or, for Python 2 only:

except (IDontLikeYouException, YouAreBeingMeanException), e:
    pass

Separating the exception from the variable with a comma will still work in Python 2.6 and 2.7, but is now deprecated and does not work in Python 3; now you should be using as.


回答 1

如何在一行中捕获多个异常(块除外)

做这个:

try:
    may_raise_specific_errors():
except (SpecificErrorOne, SpecificErrorTwo) as error:
    handle(error) # might log or have some other default behavior...

由于使用了逗号将错误对象分配给名称的较旧语法,因此需要使用括号。该as关键字用于分配。您可以为错误对象使用任何名称,我error个人更喜欢。

最佳实践

要以当前方式并与Python向前兼容的方式执行此操作,您需要使用逗号分隔Exceptions,并用括号将其包裹起来,以区别于早期的语法,后者通过遵循用逗号。

这是一个简单用法的示例:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError): # the parens are necessary
    sys.exit(0)

我仅指定这些异常以避免隐藏错误,如果遇到错误,我希望从中获得完整的堆栈跟踪。

此处记录:https : //docs.python.org/tutorial/errors.html

您可以将异常分配给变量,(e很常见,但是,如果您需要长时间的异常处理,或者您的IDE像我的一样,仅突出显示比这个更大的选择,那么您可能更喜欢使用更冗长的变量。)实例具有args属性。这是一个例子:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError) as err: 
    print(err)
    print(err.args)
    sys.exit(0)

请注意,在Python 3中,结束errexcept块时该对象不在范围内。

不推荐使用

您可能会看到用逗号分配错误的代码。不赞成使用此用法,它是Python 2.5及更早版本中唯一可用的形式,并且如果您希望代码与Python 3向前兼容,则应更新语法以使用新形式:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError), err: # don't do this in Python 2.6+
    print err
    print err.args
    sys.exit(0)

如果在代码库中看到逗号名称分配,并且您正在使用Python 2.5或更高版本,请切换到新的方式来执行此操作,以便在升级时代码保持兼容。

suppress上下文管理器

可接受的答案实际上是最少4行代码:

try:
    do_something()
except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

tryexceptpass线可以与单线处理抑制上下文管理器,可以在Python 3.4

from contextlib import suppress

with suppress(IDontLikeYouException, YouAreBeingMeanException):
     do_something()

因此,当您想pass在某些exceptions情况下使用suppress

How do I catch multiple exceptions in one line (except block)

Do this:

try:
    may_raise_specific_errors():
except (SpecificErrorOne, SpecificErrorTwo) as error:
    handle(error) # might log or have some other default behavior...

The parentheses are required due to older syntax that used the commas to assign the error object to a name. The as keyword is used for the assignment. You can use any name for the error object, I prefer error personally.

Best Practice

To do this in a manner currently and forward compatible with Python, you need to separate the Exceptions with commas and wrap them with parentheses to differentiate from earlier syntax that assigned the exception instance to a variable name by following the Exception type to be caught with a comma.

Here’s an example of simple usage:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError): # the parens are necessary
    sys.exit(0)

I’m specifying only these exceptions to avoid hiding bugs, which if I encounter I expect the full stack trace from.

This is documented here: https://docs.python.org/tutorial/errors.html

You can assign the exception to a variable, (e is common, but you might prefer a more verbose variable if you have long exception handling or your IDE only highlights selections larger than that, as mine does.) The instance has an args attribute. Here is an example:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError) as err: 
    print(err)
    print(err.args)
    sys.exit(0)

Note that in Python 3, the err object falls out of scope when the except block is concluded.

Deprecated

You may see code that assigns the error with a comma. This usage, the only form available in Python 2.5 and earlier, is deprecated, and if you wish your code to be forward compatible in Python 3, you should update the syntax to use the new form:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError), err: # don't do this in Python 2.6+
    print err
    print err.args
    sys.exit(0)

If you see the comma name assignment in your codebase, and you’re using Python 2.5 or higher, switch to the new way of doing it so your code remains compatible when you upgrade.

The suppress context manager

The accepted answer is really 4 lines of code, minimum:

try:
    do_something()
except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

The try, except, pass lines can be handled in a single line with the suppress context manager, available in Python 3.4:

from contextlib import suppress

with suppress(IDontLikeYouException, YouAreBeingMeanException):
     do_something()

So when you want to pass on certain exceptions, use suppress.


回答 2

Python文档-> 8.3处理异常

一条try语句可能具有多个except子句,以指定不同异常的处理程序。最多将执行一个处理程序。处理程序仅处理在相应的try子句中发生的异常,而不处理同一try语句的其他处理程序中的异常。exclude子句可以将多个异常命名为带括号的元组,例如:

except (RuntimeError, TypeError, NameError):
    pass

请注意,必须在该元组周围加上括号,因为除了ValueError, e:用于except ValueError as e:现代Python中通常编写的语法(如下所述)外,其他语法都是这样。为了向后兼容,仍旧支持旧语法。这意味着except RuntimeError, TypeError不等于 except (RuntimeError, TypeError):except RuntimeError as TypeError:不是您想要的。

From Python documentation -> 8.3 Handling Exceptions:

A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:

except (RuntimeError, TypeError, NameError):
    pass

Note that the parentheses around this tuple are required, because except ValueError, e: was the syntax used for what is normally written as except ValueError as e: in modern Python (described below). The old syntax is still supported for backwards compatibility. This means except RuntimeError, TypeError is not equivalent to except (RuntimeError, TypeError): but to except RuntimeError as TypeError: which is not what you want.


回答 3

如果您经常使用大量异常,则可以预定义一个元组,因此不必多次重新键入它们。

#This example code is a technique I use in a library that connects with websites to gather data

ConnectErrs  = (URLError, SSLError, SocketTimeoutError, BadStatusLine, ConnectionResetError)

def connect(url, data):
    #do connection and return some data
    return(received_data)

def some_function(var_a, var_b, ...):
    try: o = connect(url, data)
    except ConnectErrs as e:
        #do the recovery stuff
    blah #do normal stuff you would do if no exception occurred

笔记:

  1. 如果您还需要捕获除预定义元组中的异常以外的其他异常,则需要定义另一个except块。

  2. 如果您不能忍受全局变量,请在main()中定义它,并在需要的地方传递它…

If you frequently use a large number of exceptions, you can pre-define a tuple, so you don’t have to re-type them many times.

#This example code is a technique I use in a library that connects with websites to gather data

ConnectErrs  = (URLError, SSLError, SocketTimeoutError, BadStatusLine, ConnectionResetError)

def connect(url, data):
    #do connection and return some data
    return(received_data)

def some_function(var_a, var_b, ...):
    try: o = connect(url, data)
    except ConnectErrs as e:
        #do the recovery stuff
    blah #do normal stuff you would do if no exception occurred

NOTES:

  1. If you, also, need to catch other exceptions than those in the pre-defined tuple, you will need to define another except block.

  2. If you just cannot tolerate a global variable, define it in main() and pass it around where needed…


回答 4

一种方法是..

try:
   You do your operations here;
   ......................
except(Exception1[, Exception2[,...ExceptionN]]]):
   If there is any exception from the given exception list, 
   then execute this block.
   ......................
else:
   If there is no exception then execute this block. 

另一种方法是创建一种方法,该方法执行由except块执行的任务,并在except您编写的所有块中调用它。

try:
   You do your operations here;
   ......................
except Exception1:
    functionname(parameterList)
except Exception2:
    functionname(parameterList)
except Exception3:
    functionname(parameterList)
else:
   If there is no exception then execute this block. 

def functionname( parameters ):
   //your task..
   return [expression]

我知道第二种方法并不是做到这一点的最佳方法,但我只是在说明一些实现此目的的方法。

One of the way to do this is..

try:
   You do your operations here;
   ......................
except(Exception1[, Exception2[,...ExceptionN]]]):
   If there is any exception from the given exception list, 
   then execute this block.
   ......................
else:
   If there is no exception then execute this block. 

and another way is to create method which performs task executed by except block and call it through all of the except block that you write..

try:
   You do your operations here;
   ......................
except Exception1:
    functionname(parameterList)
except Exception2:
    functionname(parameterList)
except Exception3:
    functionname(parameterList)
else:
   If there is no exception then execute this block. 

def functionname( parameters ):
   //your task..
   return [expression]

I know that second one is not the best way to do this, but i’m just showing number of ways to do this thing.


如何安全地创建嵌套目录?

问题:如何安全地创建嵌套目录?

检查文件目录是否存在的最优雅方法是什么,如果不存在,则使用Python创建目录?这是我尝试过的:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

不知何故,我想念os.path.exists(感谢魔芋,布莱尔和道格拉斯)。这就是我现在所拥有的:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

是否有“打开”标志,使它自动发生?

What is the most elegant way to check if the directory a file is going to be written to exists, and if not, create the directory using Python? Here is what I tried:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

Somehow, I missed os.path.exists (thanks kanja, Blair, and Douglas). This is what I have now:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Is there a flag for “open”, that makes this happen automatically?


回答 0

在Python≥3.5上,使用pathlib.Path.mkdir

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

对于旧版本的Python,我看到两个质量很好的答案,每个都有一个小缺陷,因此我将对此进行说明:

试试看os.path.exists,然后考虑os.makedirs创建。

import os
if not os.path.exists(directory):
    os.makedirs(directory)

如注释和其他地方所述,存在竞争条件–如果在os.path.existsos.makedirs调用之间创建目录,os.makedirs则将失败并显示OSError。不幸的是,毯式捕获OSError和继续操作并非万无一失,因为它将忽略由于其他因素(例如权限不足,磁盘已满等)而导致的目录创建失败。

一种选择是捕获OSError并检查嵌入式错误代码(请参阅是否存在从Python的OSError获取信息的跨平台方法):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

或者,可以有第二个os.path.exists,但是假设另一个在第一次检查后创建了目录,然后在第二个检查之前将其删除了–我们仍然可能会上当。

取决于应用程序,并发操作的危险可能比其他因素(例如文件许可权)造成的危险更大或更小。在选择实现之前,开发人员必须了解有关正在开发的特定应用程序及其预期环境的更多信息。

现代版本的Python通过暴露FileExistsError(在3.3+ 版本中)都极大地改善了此代码。

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

…并允许关键字参数os.makedirs调用exist_ok(在3.2+版本中)。

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.

On Python ≥ 3.5, use pathlib.Path.mkdir:

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

For older versions of Python, I see two answers with good qualities, each with a small flaw, so I will give my take on it:

Try os.path.exists, and consider os.makedirs for the creation.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

As noted in comments and elsewhere, there’s a race condition – if the directory is created between the os.path.exists and the os.makedirs calls, the os.makedirs will fail with an OSError. Unfortunately, blanket-catching OSError and continuing is not foolproof, as it will ignore a failure to create the directory due to other factors, such as insufficient permissions, full disk, etc.

One option would be to trap the OSError and examine the embedded error code (see Is there a cross-platform way of getting information from Python’s OSError):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Alternatively, there could be a second os.path.exists, but suppose another created the directory after the first check, then removed it before the second one – we could still be fooled.

Depending on the application, the danger of concurrent operations may be more or less than the danger posed by other factors such as file permissions. The developer would have to know more about the particular application being developed and its expected environment before choosing an implementation.

Modern versions of Python improve this code quite a bit, both by exposing FileExistsError (in 3.3+)…

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

…and by allowing a keyword argument to os.makedirs called exist_ok (in 3.2+).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.

回答 1

Python 3.5以上版本:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir上面使用的递归方式创建目录,并且如果目录已经存在,则不会引发异常。如果不需要或不希望创建父母,请跳过该parents参数。

Python 3.2+:

使用pathlib

如果可以,请安装pathlib名为的当前反向端口pathlib2。不要安装名为的较旧的未维护的反向端口pathlib。接下来,请参考上面的Python 3.5+部分,并对其进行相同的使用。

如果使用Python 3.4,即使它附带了pathlib,它也会丢失有用的exist_ok选项。反向端口旨在提供更新的高级实现,mkdir其中包括缺少的选项。

使用os

import os
os.makedirs(path, exist_ok=True)

os.makedirs上面使用的递归方式创建目录,并且如果目录已经存在,则不会引发异常。exist_ok仅当使用Python 3.2+时,它才具有可选参数,默认值为False。在2.7之前的Python 2.x中不存在此参数。这样,就不需要像Python 2.7那样的手动异常处理。

Python 2.7+:

使用pathlib

如果可以,请安装pathlib名为的当前反向端口pathlib2。不要安装名为的较旧的未维护的反向端口pathlib。接下来,请参考上面的Python 3.5+部分,并对其进行相同的使用。

使用os

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

虽然可能会先使用朴素的解决方案,os.path.isdir然后再使用os.makedirs,但是上述解决方案颠倒了两个操作的顺序。这样,它可以防止由于创建目录的重复尝试而导致的常见竞争情况,并且还可以消除目录中文件的歧义。

请注意,捕获异常和使用errno的作用有限,因为对于文件和目录,都会引发OSError: [Errno 17] File exists,即errno.EEXIST。仅检查目录是否存在更为可靠。

选择:

mkpath创建嵌套目录,如果目录已经存在,则不执行任何操作。这适用于Python 2和3。

import distutils.dir_util
distutils.dir_util.mkpath(path)

根据Bug 10948,此替代方案的严重局限性在于,对于给定路径,每个python进程仅工作一次。换句话说,如果您使用它来创建目录,然后从Python内部或外部删除该目录,然后mkpath再次mkpath使用它来重新创建同一目录,则将仅默默地使用其先前已创建目录的无效缓存信息,而不会实际再次创建目录。相反,os.makedirs不依赖任何此类缓存。对于某些应用程序,此限制可能是可以的。


关于目录的模式,如果您关心它,请参考文档。

Python 3.5+:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir as used above recursively creates the directory and does not raise an exception if the directory already exists. If you don’t need or want the parents to be created, skip the parents argument.

Python 3.2+:

Using pathlib:

If you can, install the current pathlib backport named pathlib2. Do not install the older unmaintained backport named pathlib. Next, refer to the Python 3.5+ section above and use it the same.

If using Python 3.4, even though it comes with pathlib, it is missing the useful exist_ok option. The backport is intended to offer a newer and superior implementation of mkdir which includes this missing option.

Using os:

import os
os.makedirs(path, exist_ok=True)

os.makedirs as used above recursively creates the directory and does not raise an exception if the directory already exists. It has the optional exist_ok argument only if using Python 3.2+, with a default value of False. This argument does not exist in Python 2.x up to 2.7. As such, there is no need for manual exception handling as with Python 2.7.

Python 2.7+:

Using pathlib:

If you can, install the current pathlib backport named pathlib2. Do not install the older unmaintained backport named pathlib. Next, refer to the Python 3.5+ section above and use it the same.

Using os:

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

While a naive solution may first use os.path.isdir followed by os.makedirs, the solution above reverses the order of the two operations. In doing so, it prevents a common race condition having to do with a duplicated attempt at creating the directory, and also disambiguates files from directories.

Note that capturing the exception and using errno is of limited usefulness because OSError: [Errno 17] File exists, i.e. errno.EEXIST, is raised for both files and directories. It is more reliable simply to check if the directory exists.

Alternative:

mkpath creates the nested directory, and does nothing if the directory already exists. This works in both Python 2 and 3.

import distutils.dir_util
distutils.dir_util.mkpath(path)

Per Bug 10948, a severe limitation of this alternative is that it works only once per python process for a given path. In other words, if you use it to create a directory, then delete the directory from inside or outside Python, then use mkpath again to recreate the same directory, mkpath will simply silently use its invalid cached info of having previously created the directory, and will not actually make the directory again. In contrast, os.makedirs doesn’t rely on any such cache. This limitation may be okay for some applications.


With regard to the directory’s mode, please refer to the documentation if you care about it.


回答 2

使用tryexcept和来自errno模块的正确错误代码摆脱了竞争条件,并且是跨平台的:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

换句话说,我们尝试创建目录,但是如果它们已经存在,我们将忽略该错误。另一方面,将报告任何其他错误。例如,如果您预先创建目录’a’并从中删除所有权限,则会OSError引发errno.EACCES(权限被拒绝,错误13)。

Using try except and the right error code from errno module gets rid of the race condition and is cross-platform:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

In other words, we try to create the directories, but if they already exist we ignore the error. On the other hand, any other error gets reported. For example, if you create dir ‘a’ beforehand and remove all permissions from it, you will get an OSError raised with errno.EACCES (Permission denied, error 13).


回答 3

我个人建议您使用os.path.isdir()代替进行测试os.path.exists()

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

如果你有:

>>> dir = raw_input(":: ")

和愚蠢的用户输入:

:: /tmp/dirname/filename.etc

……你要与一个名为落得filename.etc当你传递参数os.makedirs(),如果你与测试os.path.exists()

I would personally recommend that you use os.path.isdir() to test instead of os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

If you have:

>>> dir = raw_input(":: ")

And a foolish user input:

:: /tmp/dirname/filename.etc

… You’re going to end up with a directory named filename.etc when you pass that argument to os.makedirs() if you test with os.path.exists().


回答 4

检查os.makedirs:(确保存在完整路径。)
要处理目录可能存在的事实,请catch OSError。(如果exist_okFalse(缺省值),OSError则在目标目录已经存在时引发。)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

Check os.makedirs: (It makes sure the complete path exists.)
To handle the fact the directory might exist, catch OSError. (If exist_ok is False (the default), an OSError is raised if the target directory already exists.)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

回答 5

从Python 3.5开始,pathlib.Path.mkdir有一个exist_ok标志:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

这将以递归方式创建目录,并且如果目录已经存在,则不会引发异常。

(就像从python 3.2开始os.makedirsexist_ok标志一样os.makedirs(path, exist_ok=True)

Starting from Python 3.5, pathlib.Path.mkdir has an exist_ok flag:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

This recursively creates the directory and does not raise an exception if the directory already exists.

(just as os.makedirs got an exist_ok flag starting from python 3.2 e.g os.makedirs(path, exist_ok=True))


回答 6

对这种情况的具体见解

您在特定路径下提供特定文件,然后从文件路径中提取目录。然后,在确保您拥有目录之后,尝试打开一个文件进行读取。要对此代码发表评论:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

我们要避免覆盖内置函数dir。另外,filepath或者也许fullfilepath是比它更好的语义名称,filename所以这样写会更好:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

您的最终目标是打开该文件,一开始就声明要写入,但是实际上您正在达到此目标(基于您的代码),就像这样,打开该文件进行读取

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

假设开放阅读

为什么要为您希望存在并能够读取的文件创建目录?

只是尝试打开文件。

with open(filepath) as my_file:
    do_stuff(my_file)

如果目录或文件不存在,您将获得一个IOError带有相关错误代码的:errno.ENOENT无论您使用什么平台,它都将指向正确的错误代码。您可以根据需要捕获它,例如:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

假设我们正在写作

可能就是您想要的。

在这种情况下,我们可能没有面对任何比赛条件。因此,照原样进行操作,但请注意,编写时需要使用w模式打开(或a追加)。使用上下文管理器打开文件也是Python的最佳实践。

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

但是,假设我们有几个Python进程试图将其所有数据放入同一目录。然后,我们可能会争执目录的创建。在这种情况下,最好将makedirs调用包装在try-except块中。

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Insights on the specifics of this situation

You give a particular file at a certain path and you pull the directory from the file path. Then after making sure you have the directory, you attempt to open a file for reading. To comment on this code:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

We want to avoid overwriting the builtin function, dir. Also, filepath or perhaps fullfilepath is probably a better semantic name than filename so this would be better written:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Your end goal is to open this file, you initially state, for writing, but you’re essentially approaching this goal (based on your code) like this, which opens the file for reading:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

Assuming opening for reading

Why would you make a directory for a file that you expect to be there and be able to read?

Just attempt to open the file.

with open(filepath) as my_file:
    do_stuff(my_file)

If the directory or file isn’t there, you’ll get an IOError with an associated error number: errno.ENOENT will point to the correct error number regardless of your platform. You can catch it if you want, for example:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

Assuming we’re opening for writing

This is probably what you’re wanting.

In this case, we probably aren’t facing any race conditions. So just do as you were, but note that for writing, you need to open with the w mode (or a to append). It’s also a Python best practice to use the context manager for opening files.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

However, say we have several Python processes that attempt to put all their data into the same directory. Then we may have contention over creation of the directory. In that case it’s best to wrap the makedirs call in a try-except block.

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

回答 7

试用os.path.exists功能

if not os.path.exists(dir):
    os.mkdir(dir)

Try the os.path.exists function

if not os.path.exists(dir):
    os.mkdir(dir)

回答 8

我将以下内容放下。但是,这并非完全安全。

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

现在,正如我所说,这并不是万无一失的,因为我们有可能无法创建目录,而在此期间可能会有另一个创建它的进程。

I have put the following down. It’s not totally foolproof though.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

Now as I say, this is not really foolproof, because we have the possiblity of failing to create the directory, and another process creating it during that period.


回答 9

检查目录是否存在并根据需要创建目录?

对此的直接答案是,假设有一个简单的情况,您不希望其他用户或进程弄乱您的目录:

if not os.path.exists(d):
    os.makedirs(d)

或者如果使目录符合竞争条件(即如果检查路径是否存在,则可能已经建立了其他路径),请执行以下操作:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

但是,也许更好的方法是通过以下方式使用临时目录来避免资源争用问题tempfile

import tempfile

d = tempfile.mkdtemp()

以下是在线文档中的要点:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

新的Python 3.5:pathlib.Pathexist_ok

有一个新的Path对象(从3.4版开始),它具有许多要与路径一起使用的方法-其中一个是mkdir

(在上下文中,我正在使用脚本跟踪我的每周代表。这是脚本中代码的相关部分,这些内容使我避免对同一数据每天多次遇到Stack Overflow。)

首先相关进口:

from pathlib import Path
import tempfile

我们现在不必处理os.path.join-只需将路径部分与结合起来即可/

directory = Path(tempfile.gettempdir()) / 'sodata'

然后,我确定地确保目录存在- exist_ok参数在Python 3.5中显示:

directory.mkdir(exist_ok=True)

这是文档的相关部分:

如果exist_ok为true,FileExistsErrorPOSIX mkdir -p仅当最后一个路径组件不是现有的非目录文件时,才会忽略异常(与命令相同的行为)。

这里还有更多脚本-就我而言,我不受竞争条件的影响,我只有一个进程希望目录(或包含的文件)存在,并且我没有任何尝试删除的过程目录。

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

Path必须将对象强制转换为str其他期望str路径使用它们的API 。

也许应该更新Pandas以接受抽象基类的实例os.PathLike

Check if a directory exists and create it if necessary?

The direct answer to this is, assuming a simple situation where you don’t expect other users or processes to be messing with your directory:

if not os.path.exists(d):
    os.makedirs(d)

or if making the directory is subject to race conditions (i.e. if after checking the path exists, something else may have already made it) do this:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

But perhaps an even better approach is to sidestep the resource contention issue, by using temporary directories via tempfile:

import tempfile

d = tempfile.mkdtemp()

Here’s the essentials from the online doc:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

New in Python 3.5: pathlib.Path with exist_ok

There’s a new Path object (as of 3.4) with lots of methods one would want to use with paths – one of which is mkdir.

(For context, I’m tracking my weekly rep with a script. Here’s the relevant parts of code from the script that allow me to avoid hitting Stack Overflow more than once a day for the same data.)

First the relevant imports:

from pathlib import Path
import tempfile

We don’t have to deal with os.path.join now – just join path parts with a /:

directory = Path(tempfile.gettempdir()) / 'sodata'

Then I idempotently ensure the directory exists – the exist_ok argument shows up in Python 3.5:

directory.mkdir(exist_ok=True)

Here’s the relevant part of the documentation:

If exist_ok is true, FileExistsError exceptions will be ignored (same behavior as the POSIX mkdir -p command), but only if the last path component is not an existing non-directory file.

Here’s a little more of the script – in my case, I’m not subject to a race condition, I only have one process that expects the directory (or contained files) to be there, and I don’t have anything trying to remove the directory.

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

Path objects have to be coerced to str before other APIs that expect str paths can use them.

Perhaps Pandas should be updated to accept instances of the abstract base class, os.PathLike.


回答 10

在Python 3.4中,您还可以使用全新的pathlib模块

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.

In Python 3.4 you can also use the brand new pathlib module:

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.

回答 11

相关的Python文档建议使用的编码风格(更容易请求原谅比许可)EAFP。这意味着代码

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

比替代品更好

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

该文档正是由于此问题中讨论的种族条件而提出了这一建议。此外,正如此处其他人所提到的,查询一次操作系统而不是两次查询操作系统具有性能优势。最后,在某些情况下(当开发人员知道应用程序正在运行的环境时),可能会提出支持第二个代码的参数,只有在特殊情况下才提倡该程序已为该程序建立了私有环境。本身(以及同一程序的其他实例)。

即使在这种情况下,这也是一种不好的做法,并且可能导致长时间的无用调试。例如,我们为目录设置权限的事实不应该使我们拥有为我们目的而适当设置的印象权限。可以使用其他权限挂载父目录。通常,程序应始终正常运行,并且程序员不应期望一个特定的环境。

The relevant Python documentation suggests the use of the EAFP coding style (Easier to Ask for Forgiveness than Permission). This means that the code

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

is better than the alternative

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

The documentation suggests this exactly because of the race condition discussed in this question. In addition, as others mention here, there is a performance advantage in querying once instead of twice the OS. Finally, the argument placed forward, potentially, in favour of the second code in some cases –when the developer knows the environment the application is running– can only be advocated in the special case that the program has set up a private environment for itself (and other instances of the same program).

Even in that case, this is a bad practice and can lead to long useless debugging. For example, the fact we set the permissions for a directory should not leave us with the impression permissions are set appropriately for our purposes. A parent directory could be mounted with other permissions. In general, a program should always work correctly and the programmer should not expect one specific environment.


回答 12

Python3中os.makedirs支持设置exist_ok。默认设置为False,这意味着OSError如果目标目录已存在,将引发。通过设置exist_okTrueOSError(目录存在)将被忽略,并且不会创建目录。

os.makedirs(path,exist_ok=True)

Python2中os.makedirs不支持设置exist_ok。您可以在heikki-toivonen的答案中使用该方法:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

In Python3, os.makedirs supports setting exist_ok. The default setting is False, which means an OSError will be raised if the target directory already exists. By setting exist_ok to True, OSError (directory exists) will be ignored and the directory will not be created.

os.makedirs(path,exist_ok=True)

In Python2, os.makedirs doesn’t support setting exist_ok. You can use the approach in heikki-toivonen’s answer:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

回答 13

对于单线解决方案,可以使用IPython.utils.path.ensure_dir_exists()

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

文档中确保目录存在。如果不存在,请尝试创建它,并在其他进程正在这样做的情况下防止出现竞争情况。

For a one-liner solution, you can use IPython.utils.path.ensure_dir_exists():

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

From the documentation: Ensure that a directory exists. If it doesn’t exist, try to create it and protect against a race condition if another process is doing the same.


回答 14

您可以使用 mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

请注意,它也会创建祖先目录。

它适用于Python 2和3。

You can use mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

Note that it will create the ancestor directories as well.

It works for Python 2 and 3.


回答 15

我使用os.path.exists()是一个Python 3脚本,可用于检查目录是否存在,如果目录不存在则创建一个,如果目录存在则将其删除(如果需要)。

它提示用户输入目录,并且可以轻松修改。

I use os.path.exists(), here is a Python 3 script that can be used to check if a directory exists, create one if it does not exist, and delete it if it does exist (if desired).

It prompts users for input of the directory and can be easily modified.


回答 16

您可以os.listdir为此使用:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')

You can use os.listdir for this:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')

回答 17

我找到了这个问题,起初我为自己遇到的一些失败和错误感到困惑。我正在使用Python 3(Arch Linux x86_64系统上的Anaconda虚拟环境中的v.3.5)。

考虑以下目录结构:

└── output/         ## dir
   ├── corpus       ## file
   ├── corpus2/     ## dir
   └── subdir/      ## dir

这是我的实验/注释,它们使事情变得清晰:

# ----------------------------------------------------------------------------
# [1] /programming/273192/how-can-i-create-a-directory-if-it-does-not-exist

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

结论:我认为“方法2”更可靠。

[1] 如果目录不存在,如何创建?

[2] https://docs.python.org/3/library/os.html#os.makedirs

I found this Q/A and I was initially puzzled by some of the failures and errors I was getting. I am working in Python 3 (v.3.5 in an Anaconda virtual environment on an Arch Linux x86_64 system).

Consider this directory structure:

└── output/         ## dir
   ├── corpus       ## file
   ├── corpus2/     ## dir
   └── subdir/      ## dir

Here are my experiments/notes, which clarifies things:

# ----------------------------------------------------------------------------
# [1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

Conclusion: in my opinion, “Method 2” is more robust.

[1] How can I create a directory if it does not exist?

[2] https://docs.python.org/3/library/os.html#os.makedirs


回答 18

我看到了Heikki ToivonenABB的答案,并想到了这种变化。

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST or not os.path.isdir(path):
            raise

I saw Heikki Toivonen and A-B-B‘s answers and thought of this variation.

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST or not os.path.isdir(path):
            raise

回答 19

使用此命令检查并创建目录

 if not os.path.isdir(test_img_dir):
     os.mkdir(test_img_dir)

Use this command check and create dir

 if not os.path.isdir(test_img_dir):
     os.mkdir(test_img_dir)

回答 20

如果在支持mkdir-p选项命令的计算机上运行,​​为什么不使用子流程模块 ?适用于python 2.7和python 3.6

from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])

在大多数系统上都可以做到。

在可移植性无关紧要的情况下(例如,使用docker),解决方案只需2行。您也不必添加逻辑来检查目录是否存在。最后,重新运行很安全,没有任何副作用

如果您需要错误处理:

from subprocess import check_call
try:
    check_call(['mkdir', '-p', 'path1/path2/path3'])
except:
    handle...

Why not use subprocess module if running on a machine that supports command mkdir with -p option ? Works on python 2.7 and python 3.6

from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])

Should do the trick on most systems.

In situations where portability doesn’t matter (ex, using docker) the solution is a clean 2 lines. You also don’t have to add logic to check if directories exist or not. Finally, it is safe to re-run without any side effects

If you need error handling:

from subprocess import check_call
try:
    check_call(['mkdir', '-p', 'path1/path2/path3'])
except:
    handle...

回答 21

如果考虑以下因素:

os.path.isdir('/tmp/dirname')

表示目录(路径)存在,并且是目录。所以对我来说,这种方式满足了我的需求。因此,我可以确保它是文件夹(不是文件)并且存在。

If you consider the following:

os.path.isdir('/tmp/dirname')

means a directory (path) exists AND is a directory. So for me this way does what I need. So I can make sure it is folder (not a file) and exists.


回答 22

create_dir()在程序/项目的入口点调用该函数。

import os

def create_dir(directory):
    if not os.path.exists(directory):
        print('Creating Directory '+directory)
        os.makedirs(directory)

create_dir('Project directory')

Call the function create_dir() at the entry point of your program/project.

import os

def create_dir(directory):
    if not os.path.exists(directory):
        print('Creating Directory '+directory)
        os.makedirs(directory)

create_dir('Project directory')

回答 23

您必须在创建目录之前设置完整路径:

import os,sys,inspect
import pathlib

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"

if not os.path.exists(your_folder):
   pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

这对我有用,希望对您也一样

You have to set the full path before creating the directory:

import os,sys,inspect
import pathlib

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"

if not os.path.exists(your_folder):
   pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

This works for me and hopefully, it will works for you as well


回答 24

import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

您的代码在哪里使用(touch)命令

这将检查文件是否存在,如果不存在则将创建它。

import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

Where your code here is use the (touch) command

This will check if the file is there if it is not then it will create it.