V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Leon6868
V2EX  ›  Python

用 try...except...提高代码的稳定性,是否是好习惯?

  •  1
     
  •   Leon6868 · 2020-05-29 19:25:08 +08:00 · 8552 次点击
    这是一个创建于 1668 天前的主题,其中的信息可能已经有所发展或是发生改变。

    把每个可能出错的东西都套上 try...except,这样有什么坏处吗?
    比如,是否会影响代码的执行效率?

    48 条回复    2020-06-01 12:06:45 +08:00
    Leigg
        1
    Leigg  
       2020-05-29 19:28:18 +08:00 via Android
    看情况吧,有些异常是必须暴露的
    Leigg
        2
    Leigg  
       2020-05-29 19:28:40 +08:00 via Android
    except 也要加上具体的 err
    whi147
        3
    whi147  
       2020-05-29 19:29:19 +08:00 via iPhone
    你好,是的。
    xulolololololo
        4
    xulolololololo  
       2020-05-29 19:29:38 +08:00 via Android
    不要随便 catch,必须做一些真实的假设,如果和假设不符合,就是 bug 应该暴露出来被修复
    MeteorCat
        5
    MeteorCat  
       2020-05-29 19:29:54 +08:00 via Android
    我同意看情况来使用,确实有的异常是必要的
    Leon6868
        6
    Leon6868  
    OP
       2020-05-29 19:30:05 +08:00
    @Leigg 没加上具体的 error 会有什么坏处吗,比如影响可读性?
    qile1
        7
    qile1  
       2020-05-29 19:54:03 +08:00 via Android
    @Leon6868 python 里面强调不要这样使用
    try
    execpt E*** as e:
    print ( e )
    我反正是就这样用了,有些异常很难想到,不这样处理后台服务类程序可能直接退出,所以为了不出现程序自动退出情况,我选择使用大量 try 来规避,对性能影响不太大,之前好像看到过有人做测试,使用 try 好像影响不大
    Immortal
        8
    Immortal  
       2020-05-29 20:00:47 +08:00 via Android   ❤️ 4
    很多人吐槽和嘲笑的 golang err 处理 反而我看来是成语健壮稳定的保证 传统的 try catch 自我安慰的成分挺高
    Immortal
        9
    Immortal  
       2020-05-29 20:01:12 +08:00 via Android
    @Immortal 成语->程序
    xujinkai
        10
    xujinkai  
       2020-05-29 20:02:54 +08:00 via Android   ❤️ 2
    给用户使用的程序,我会在最外层加以防程序莫名崩了。给程序员的库,要是用的太随意,会导致本该报错的地方莫名出现 bug 还不好找。
    hdbzsgm
        11
    hdbzsgm  
       2020-05-29 20:07:11 +08:00
    @Immortal #8 这边建议了解一下 java
    cozof
        12
    cozof  
       2020-05-29 20:07:51 +08:00 via iPhone
    python 没有全局捕获异常的方式么,一般写个中间件全局捕获异常记录日志,在配置文件做个开关,生产环境打开,开发环境关闭,直接抛异常。
    Jirajine
        13
    Jirajine  
       2020-05-29 20:08:36 +08:00 via Android
    @Immortal 去掉异常控制流把错误由返回值统一处理确实是进步,但 go 的简陋导致错误处理太 verbose 了,rust 那种包装在一个 Result 枚举里面就方便很多,预计 go2 也会增加一些语法糖来简化。
    superrichman
        14
    superrichman  
       2020-05-29 20:29:28 +08:00 via iPhone
    @cozof 可以用装饰器实现
    GeruzoniAnsasu
        15
    GeruzoniAnsasu  
       2020-05-29 20:48:44 +08:00 via Android   ❤️ 10
    哎。。。
    为什么看起来好多人都不明白异常处理是干啥用的
    建议学习一下 gcc 异常处理的实现方式感受一下


    从程序执行流本身来看,异常的意义在于回滚调用栈

    回滚调用栈。

    如果你不确定是不是该上异常处理,那就判断一下此时是不是想把调用栈回滚到某个状态前。一个用异常处理的绝佳例子是 visitor 设计模式,当递归 visit 到某一个节点发生错误时,显然把 error code 穿过层层递归跟等于 nil 的 result 一起传回来是个非常蠢的做法(你甚至得给每一种节点类型的 visit 函数都写一遍 if err return ! shit !)


    从业务逻辑上来看,异常处理机制是用来统一处理某封闭模块在各个阶段产生的同类错误用的

    比如游戏读图,在每个关卡我要读不同的地图文件,文件读上来以后要解析数据结构,有可能读图失败,有可能结构不对,出错了以后我想让游戏回退到选图界面,那我肯定没理由在写游戏逻辑的地方还判断 loadNextStage()成功了没有,抛出异常可以让更上层的逻辑来处理同时打断游戏逻辑,其实还是对应回调用栈回滚的本质



    再回头看为什么指南不建议你写 try except print 是不是理解一些了
    reus
        16
    reus  
       2020-05-29 21:21:30 +08:00
    这个就是纸尿裤一样的东西,有些人要用,但会控制大小便的人不需要。
    Narcissu5
        17
    Narcissu5  
       2020-05-29 21:33:20 +08:00   ❤️ 1
    一个基本原则就是只 catch 你能处理的异常,捕获异常之后应该进行处理然后程序继续运行。如果是无法处理的异常,比方连不上数据库了,则使用 let it crash 的原则,让整个过程失败并回滚,阻止异常进一步扩散
    hakono
        18
    hakono  
       2020-05-29 22:03:08 +08:00
    建议楼主了解下 Let it crash 的思想,这个思想对理解异常处理很有帮助

    出错了?出错了就出错了呗,崩了就崩了呗,崩了的时候你至少知道系统出错了。总好过你用 try exception 乱包一气,然后系统出错了还不明不白继续执行下去,等你注意到的时候可能已经过了好久,数据都因此乱七八糟了要好。

    当然这里不是说你不该用 try,只是什么 try exception 的话对系统来说是个灾难
    ruyu
        19
    ruyu  
       2020-05-29 22:15:24 +08:00
    个人认为要尽可能地暴露问题, 但是要防止问题扩大化
    akazure
        20
    akazure  
       2020-05-29 22:28:18 +08:00
    try final 应该更有意义吧
    agagega
        21
    agagega  
       2020-05-29 23:01:23 +08:00
    断言、异常和错误码(或者 Maybe 类型)是三种用途:
    1. 断言是「绝对不应该出现的错误」,如果出现了,表明是你的代码有 bug 。换言之,断言的作用是辅助 debug
    2. 异常是「不太可能出现,出现了我也不太能继续处理」的问题,比如内存不够了,网络断了
    3. 错误码是「不希望它出现,但出现了我也应该要处理」的问题,比如错误的用户输入

    Java 里面似乎会用异常来负责一些错误码的功能,Python 听说甚至会用异常来代替部分控制流。但总体的思路就是这样。
    fancy2020
        22
    fancy2020  
       2020-05-29 23:06:05 +08:00
    我觉得用 try catch 的最大前提是想清楚,这个错误应该在系统的哪一个层级来处理~
    上边有人说到 let it crash 的思想,我觉得这是因为在 Erlang 中每一个轻量级进程崩了并不影响整个系统的运行(其实相当于是 Erlang 虚拟机帮你有个兜底的 catch),像其他语言如果任由你的代码 crash 那肯定是不行的。
    liyongqiang1995
        23
    liyongqiang1995  
       2020-05-29 23:12:53 +08:00
    个人理解,可能出错的地方要 try, 如果是预料中可能发生的错误且不影响继续运行就不 raise,否则打印一定上下文再 raise
    UnknownR
        24
    UnknownR  
       2020-05-29 23:27:18 +08:00
    用 try catch,是为了规避异常抛出而打断持续进程,有些地方的 exception 如果对现有进程没有影响,那你可以直接 catch 然后作为一个记录。

    如果你需要进程从头到尾按你的需求执行,不写 catch 块让他自然抛出才是建议的做法,但是比如你要执行一个循环查询,有异常只需记录你又不能枚举所有异常类型,那可以直接 catch 并存储。
    ZRS
        25
    ZRS  
       2020-05-29 23:43:32 +08:00
    只要别 except Exception 就行。。
    Jirajine
        26
    Jirajine  
       2020-05-29 23:56:17 +08:00 via Android
    @GeruzoniAnsasu 你这样说确实有道理,让我想到所谓大部分错误都是暂时的,重启能解决 90%的问题。elixir 文档里说的遇到错误就快速退出然后重启,让系统回到一个已知的正确的状态,从而使整个系统具有很高的容错性。

    但任何事情都有 trade off,抛异常直接跳出来确实比插一根管子层层返回错误要优雅,但也太过狂野。
    当进行一个可能失败的操作时,使用异常就得包到一层 try 里面,错误跳到 catch 里处理,如果我想失败重试,整个控制流就会非常混乱。
    这种情况直接返回一个包含了成功时的结果或失败时的原因的结构则要清晰很多,无论是直接裸返回再手写 iferr 还是包到 Result 里面用 pattern match,错误处理包含在常规控制流中。
    日常使用时这种情况占绝大多数,所以较新的语言开始用直接返回替代异常控制流作为绝大多数情况下的错误处理方式,当然少部分时候有时也确实需要回滚,所以也有提供类似异常的 panic recover 机制,我觉得这是正确的发展方向。
    hellov22ex
        27
    hellov22ex  
       2020-05-30 00:08:37 +08:00
    少用这个东西,除非是你自己很熟悉的代码,不然你不知道会导致软件系统产生什么奇怪的状态。
    love
        28
    love  
       2020-05-30 00:33:22 +08:00 via Android
    骚年,你这做法完全和各种编程实践文章背道而驰了,应该只处理预期的必要异常,其他都要往上穿,是否要在顶层崩溃掉程序看实际程序业务
    sazima
        29
    sazima  
       2020-05-30 00:42:06 +08:00
    try:
    xxx
    except Exception:
    logger.warn('......')
    raise
    ArtIsPatrick
        30
    ArtIsPatrick  
       2020-05-30 00:49:26 +08:00 via iPhone
    当然不是,大部分情况在最外层用装饰器( Python )或者切面( Java )处理异常就行了,里面根据情况看是否需要抛出特定的异常,否则不必再写 try catch 。
    zhuangzhuang1988
        31
    zhuangzhuang1988  
       2020-05-30 01:04:09 +08:00
    没有必然联系.
    maomaomao001
        32
    maomaomao001  
       2020-05-30 03:21:23 +08:00
    肯定不能到处 try catch 啊 , 程序(函数 ,模块) 入口部分检查好, 中间纯逻辑部分你在检查 try catch 肯定是很严重的设计错误
    CismonX
        33
    CismonX  
       2020-05-30 03:55:07 +08:00
    try:
    # ...
    except Exception:
    pass

    公司项目里面都是这样的代码。。
    lizytalk
        34
    lizytalk  
       2020-05-30 07:02:45 +08:00 via iPhone
    @Leon6868 如果不把具体错误打出来当发生故障的时候你不知道故障在哪
    crclz
        35
    crclz  
       2020-05-30 08:03:10 +08:00
    https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exception-throwing
    ❌ DO NOT return error codes.
    ✔️ DO report execution failures by throwing exceptions.

    ❌ DO NOT use exceptions for the normal flow of control, if possible.
    For example, you can provide a way to check preconditions before calling a member so users can write code that does not throw exceptions.

    The member used to check preconditions of another member is often referred to as a [tester] , and the member that actually does the work is called a [doer] .

    Tester-Doer Pattern https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions-and-performance
    xingheng
        36
    xingheng  
       2020-05-30 08:20:12 +08:00 via iPhone
    @GeruzoniAnsasu 在业务层“回滚调用栈”应该是手动 raise custom eexception 吧,这样理解应该没问题吧
    weyou
        37
    weyou  
       2020-05-30 09:13:16 +08:00 via Android
    @cozof @superrichman 最简单就是 sys.excepthook
    mjikop1231
        38
    mjikop1231  
       2020-05-30 09:21:43 +08:00
    然后跑去 GO,emmmm
    xuanbg
        39
    xuanbg  
       2020-05-30 09:22:49 +08:00
    尽量少用,除非是可以预见且应该处理的异常,譬如网络请求超时这种。
    janxin
        40
    janxin  
       2020-05-30 10:18:29 +08:00
    try/catch 是需要你按照一定规律处理错误的,部分错误也是必须要向上抛出的。

    具体是你可能需要根据不同错误进行不同处理
    xujiaze
        41
    xujiaze  
       2020-05-30 10:46:21 +08:00
    try except 还是很有必要的,但是不是万能的解决方案
    同意其他人的回答,不要过度依赖
    bingmang
        42
    bingmang  
       2020-05-30 13:06:53 +08:00
    go 就没这个烦恼
    movistar
        43
    movistar  
       2020-05-30 13:19:22 +08:00
    考虑好自己写的代码是不是核心关键逻辑,不是核心逻辑那就都加上 try catch/except
    不要扩大影响面,比如有一个接口可能向下游拿十几个 RPC,你加了个功能,结果出异常,难道就 let it crash 让一个不是关键流程直接搞挂整个接口没有任何返回?
    如果整条链路每个信息都很重要,那可以,没问题,let it crash 直接在线程顶层 try catch/except 不要让服务崩掉就可以了

    否则建议全部都加上...

    任何现代化的语言都不至于 catch 一下就不知道出错在哪,打个 ERROR Log,监控 ERROR Log 量,不要放任 ERROR 就行了

    当然取决于你什么行业,什么方向,不同方向对于服务要求不同....
    ayavvv
        44
    ayavvv  
       2020-05-30 14:00:12 +08:00
    try catch 我一般最外层才加,要不然很多时候内层代码把异常吃掉了,再去翻日志很麻烦
    EscYezi
        45
    EscYezi  
       2020-05-30 17:38:04 +08:00 via iPhone
    没有必要全部加上。只有在不能对外直接抛出异常时才必须尝试捕获异常,比如返回响应给用户之前。大部分异常即使捕获了也处理不了,还不如速错;如果是能处理的则应该提前检测是否可能出错,而不是等抛异常了再进行处理。
    wzwwzw
        46
    wzwwzw  
       2020-05-30 22:11:02 +08:00
    内部能处理的异常就 try 否则就抛给调用方。
    opengps
        47
    opengps  
       2020-05-31 11:45:43 +08:00 via Android
    根据需求选用,我最早入门时候,就是各种包裹才能实现出现故障不退出,因为我的 GPS 服务器需要长久存活才能不因为个别数据包错误中断
    xpresslink
        48
    xpresslink  
       2020-06-01 12:06:45 +08:00
    try...except...提高代码的稳定性
    这个说法是存疑的,try...except..本身并不能提高代码的稳定性
    如果用了 try 就要识别处理,识别和处理的水平决定代码的稳定性

    比如你直接把异常直接弄成 Exception 完全没有细化,又不去有针对性处理,还不如直接报错还能知道为什么错了。
    但是凡事都有代价,写得 try 越多无效率代码就会浪费越多。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2828 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 12:16 · PVG 20:16 · LAX 04:16 · JFK 07:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.