【问题标题】:how to close python script without just activating "except" in try/except statement如何关闭 python 脚本而不只是在 try/except 语句中激活“except”
【发布时间】:2016-09-23 17:32:16
【问题描述】:

我有一个 python 脚本在数据库中运行大约 50,000 个项目,每个项目大约需要 10 秒,我想停止它。

但是,如果我只是在代码在 try except 部分中时按 ctrl + C,那么我的程序会进入期望语句并删除该项目然后继续。关闭程序的唯一方法就是以这种方式反复删除项目,直到我运气好,没有在 try 语句中捕获它。

如何在不终止 try 语句的情况下退出脚本?

编辑 1

我有一个类似这样的声明:

for item in items:
   try:
      if is_item_gone(content) == 1:
         data = (item['id'])
         db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
      else:
          do alot of stuff
   except:
      data = (item['id'])
      db.delete('...')

编辑 2

我的连接到数据库代码的顶部如下所示:

#!/usr/bin/python
import MySQLdb
import sys

class Database:
....

【问题讨论】:

  • 使用except Exception 而不是except
  • 你发现了什么异常?
  • 这个问题几乎可以肯定是因为你使用了一个毯子 except 这几乎总是一个坏主意,如果你捕捉到特定的异常然后 ctrl + C 将停止你的程序跨度>
  • @PadraicCunningham 我现在添加了我的代码。
  • 是的,你的毯子除了是问题,你用什么连接到数据库?基本上你应该有except some_specific_exceptionexcept some_specific_exception, another_specific_exception,因为你捕捉到了每一个异常,你捕捉到了键盘中断,所以脚本继续运行

标签: python


【解决方案1】:
import signal
import MySQLdb
import sys

## Handle process signals:
def sig_handler(signal, frame):
    global connection
    global cursor

    cursor.close()
    connection.close()

    exit(0)

## Register a signal to the handler:
signal.signal(signal.SIGINT, sig_handler)

class Database:
    # Your class stuf here.
    # Note that in sig_handler() i close the cursor and connection,
    # perhaps your class has a .close call? If so use that.

for item in items:
   try:
      if is_item_gone(content) == 1:
         data = (item['id'])
         db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
      else:
          do alot of stuff
   except MySQLdb.Error  as e:
      data = (item['id'])
      db.delete('...')

这将在任何给定时间注册并捕获例如 Ctrl+c。处理程序将相应地终止一个套接字,然后以退出代码 0 退出,这是一个很好的退出代码。

这是一种更“全局”的方法来捕捉键盘中断。

【讨论】:

  • 如果您花时间投反对票,请做一个好运动并解释原因。我们中的一些人实际上正在努力改进,如果没有适当的反馈,很难做到这一点。 (注意到反对票现已消失,但如果您正在阅读本文或计划反对投票,请写下便条)。
  • 我没有对你投反对票,但我认为你的解决方案并不像 pythonic。首先,OP 不应该捕获所有异常,而应该更具体。其次,对于 Ctrl-C 有一个要捕获的异常,这将使 Ctrl-C 处理代码保持在您要捕获它的块的本地:stackoverflow.com/questions/15318208/…
  • @rrauenza 我想从技术上讲这不是“Pythonic”,但它是线程友好的和 *nix 捕获事件的标准方式,例如 Ctrl+C(实际上,这是一个终止信号,什么都没有别的)。 try 在我看来应该保留给孤立事件或实际代码问题,而不是用户/外部输入。但是话又说回来,人们似乎喜欢 try 的捕获量就像人们喜欢 goto 在 Visual Basic 时代一样。我不知道,我想我变老了,脾气暴躁。无论如何,问题已回答并标记为已解决,我猜这里的工作已经完成。
  • @Rorschach 是的,此代码应位于脚本的顶部,以捕获任何类型的“终止信号”。安全地停止线程、终止套接字而不必在丑陋的try catch 中包装一大块代码(while 循环等)非常有用。 Try catchs 很好,但要针对不同的情况使用适当的方法。在这种情况下,您想要捕获实际的 SIGHUP 信号 (Ctrl+C),这是操作系统定义的用户输入以终止脚本。当然有一个 try 捕获它,但从本质上讲,它是一个信号,请这样对待它。
  • 请注意,您实际上不必抓住KeyboardInterrupt 来让Ctrl-C 工作...您不必意外 用毯子except: 抓住它.
【解决方案2】:

问题是因为你使用了一个毯子except,这总是一个坏主意,如果你捕捉到特定的异常,那么当你KeyboardInterrupt你的脚本将停止:

for item in items:
   try:
      if is_item_gone(content) == 1:
         data = (item['id'])
         db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
      else:
          do alot of stuff
   except MySQLdb.Error  as e:
      print(e)
      data = (item['id'])
      db.delete('...')

如果您有其他要捕获的异常,您可以在 except 中使用多个:

 except (KeyError, MySQLdb.Error) as e

至少你可以捕捉到Exception as e 并打印错误。

for item in items:
   try:
      if is_item_gone(content) == 1:
         data = (item['id'])
         db.update('UPDATE items SET empty = 0 WHERE id = (%s);', data)
      else:
          do alot of stuff
   except Exception as e:  
      print(e)
      data = (item['id'])
      db.delete('...')

这个故事的寓意是不要使用毯子,除非抓住你所期望的并记录错误也可能是一个好主意。 exception-hierarchy 也值得一读,看看你到底在抓什么。

【讨论】:

  • 仅供参考:isinstance(KeyboardInterrupt, Exception) -> False。 KeyboardInterrupt 不是从 Exception 派生的。
  • @rrauenza,没错,它直接继承自 BaseException,GeneratorExit 和 SystemExit 也是如此
【解决方案3】:

我会这样重写:

try:
    # ...
except KeyboardInterrupt:
    sys.exit(1)
except Exception:  # I would use a more specific exception here if you can.
    data = (item['id'])
    db.delete('...')

或者只是

try:
    # ...
except Exception:  # I would use an even more specific exception here if you can.
    data = (item['id'])
    db.delete('...')

可以在此处找到 Python 2 的异常层次结构:https://docs.python.org/2/library/exceptions.html#exception-hierarchy

此外,许多脚本的底部都写有类似这样的样板:

def _main(args):
   ...

if __name__ == '__main__':
    try:
        sys.exit(_main(sys.argv[1:]))
    except KeyboardInterrupt:
        print >> sys.stderr, '%s: interrupted' % _SCRIPT_NAME
        sys.exit(1)

当您在主体中使用except: 而不是except Exception: 时,您不允许此顶级异常块捕获KeyboardInterrupt。 (您实际上并没有去捕捉它以使 Ctrl-C 起作用——这只会使KeyboardInterrupt 被提高时更漂亮。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    • 2020-11-20
    • 2011-09-29
    • 2019-06-26
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多