捕获并打印完整的 Python 异常回溯,而无需停止/退出程序

Catch and print full Python exception traceback without halting/exiting the program

提问人:chriscauley 提问时间:9/14/2010 最后编辑:Asclepiuschriscauley 更新时间:10/14/2023 访问量:1386945

问:

我想在不退出的情况下捕获和记录异常,例如,

try:
    do_stuff()
except Exception as err:
    print(Exception, err)
    # I want to print the entire traceback here,
    # not just the exception name and details

我想打印与引发异常时打印的输出完全相同的输出,而没有 try/except 拦截异常,并且我不希望它退出我的程序。

Python 异常 try-catch 回溯

评论

16赞 Vito Gentile 2/25/2021
不是一个完整的答案,但有人可能想知道您可以访问大量信息(至少在 Python 3.x 中)err.__traceback__
13赞 Olivier Pons 6/2/2021
似乎我是世界上唯一一个想在没有错误的情况下打印堆栈的人(= 只是为了看看我是如何到达这个精确的行的(这不是我的代码,而且它太丑了,我无法弄清楚它是如何来到这里的!
0赞 endive1783 11/9/2023
@OlivierPons可能是非常糟糕的做法,但添加一条“1/0”线,你就会明白是什么让你;)

答:

9赞 Ivo van der Wijk 9/14/2010 #1

您需要将 try/except 放在可能发生错误的最内部循环中,即

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

...等等

换句话说,您需要将可能在 try/except 中失败的语句包装在尽可能具体的内部循环中。

3赞 nmichaels 9/14/2010 #2

您需要回溯模块。它可以让您像 Python 通常那样打印堆栈转储。具体而言,print_last 函数将打印最后一个异常和堆栈跟踪。

1252赞 volting 9/14/2010 #3

traceback.format_exc() 或 sys.exc_info() 将产生更多信息,如果这是您想要的。

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

评论

22赞 weberc2 8/28/2019
print(sys.exc_info()[0]指纹。<class 'Exception'>
15赞 droid192 9/9/2019
不要使用 exc...回溯包含 stackoverflow.com/questions/4564559/ 的所有信息...
36赞 Teepeemm 10/14/2020
print(sys.exc_info()[2])收益 率。<traceback object at 0x0000028A79E6B2C8>
10赞 Mark 5/8/2021
print(traceback.format_exc())比 更好。 返回整个元组,看起来 So 确实非常出色,因为打印traceback.print_tb(exc.__traceback__)print(sys.exc_info())(<class 'UnicodeDecodeError'>, UnicodeDecodeError('utf-8', b'\x81', 0, 1, 'invalid start byte'), <traceback object at 0x7f179d64ae00>)traceback.format_exc()Traceback (most recent call last): File "<ipython-input-15-9e3d6e01ef04>", line 2, in <module> b"\x81".decode() UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 0: invalid start byte
7赞 usamec 8/2/2022
删除它不起作用。exc_info()
838赞 Sylvain Leroux 6/6/2013 #4

其他一些答案已经指出了回溯模块。

请注意,在某些极端情况下,您将无法获得您期望的东西。在 Python 2.x 中:print_exc

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

...将显示上一个异常的回溯:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

如果您确实需要访问原始回溯一种解决方案是将exc_info返回的异常信息缓存在局部变量中,并使用以下print_exception显示它:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

生产:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

不过,这有一些陷阱:

  • sys_info的文档:

    将回溯返回值分配给正在处理异常的函数中的局部变量将导致循环引用。这将防止同一函数中的局部变量或回溯引用的任何内容被垃圾回收。[...]如果您确实需要回溯,请确保在使用后将其删除(最好尝试一下......finally语句)

  • 但是,来自同一个文档:

    从 Python 2.2 开始,当启用垃圾回收并且它们变得无法访问时,此类循环会自动回收,但避免创建循环仍然更有效。


另一方面,通过允许您访问与异常关联的回溯,Python 3 产生了一个不那么令人惊讶的结果:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

...将显示:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
360赞 dimo414 4/29/2015 #5

如果正在调试,只想查看当前堆栈跟踪,只需调用:

traceback.print_stack()

无需手动引发异常只是为了再次捕获它。

评论

15赞 pppery 11/8/2015
traceback 模块正是这样做的 - 引发并捕获异常。
6赞 mpen 12/11/2018
顺便说一句,默认情况下,输出转到 STDERR。没有出现在我的日志中,因为它被重定向到其他地方。
1赞 x-yuri 6/26/2020
@pppery 我无法用 python 3.8 看到它。而 和 的问题是它不显示完整的回溯,只显示从 到 。trycatchraiseexcept
221赞 Russia Must Remove Putin 7/16/2015 #6

如何在不停止程序的情况下打印完整的回溯?

当您不想在出现错误时停止程序时,您需要使用 try/except 来处理该错误:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

为了提取完整的回溯,我们将使用标准库中的模块:traceback

import traceback

为了创建一个相当复杂的堆栈跟踪来证明我们得到了完整的堆栈跟踪:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

印刷

若要打印完整的回溯,请使用以下方法:traceback.print_exc

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

其中打印:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

比打印、记录更好:

但是,最佳做法是为您的模块设置一个记录器。它将知道模块的名称并能够更改级别(以及其他属性,例如处理程序)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

在这种情况下,您将需要该函数:logger.exception

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

其中日志:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

或者,也许你只想要字符串,在这种情况下,你会想要这个函数:traceback.format_exc

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

其中日志:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

结论

对于所有三个选项,我们看到我们得到的输出与出现错误时相同:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

使用哪个

性能问题在这里并不重要,因为 IO 通常占主导地位。我更喜欢,因为它以向前兼容的方式精确地执行所请求的:

logger.exception(error)

可以调整记录电平和输出,无需接触代码即可轻松关闭。通常,做直接需要的事情是最有效的方法。

评论

6赞 herve-guerin 11/23/2017
如上所述,对我来说也是如此,只返回最后一个调用:你如何成功地返回堆栈的几个级别(可能还有所有级别?traceback.print_exc()
0赞 Russia Must Remove Putin 11/23/2017
@geekobi 我不确定你在这里问什么。我演示了我们可以回溯到程序/解释器的入口点。你不清楚什么?
2赞 fizloki 8/22/2018
@geekobi说的是,如果你抓住并重新加注,traceback.print_exc() 只会返回重新加注的堆栈,而不是原始的堆栈。
0赞 Russia Must Remove Putin 8/22/2018
@fizloki你是如何“养育”的?您是在进行裸链或异常链接,还是隐藏原始回溯?看到 stackoverflow.com/questions/2052390/...raise
1赞 Russia Must Remove Putin 7/16/2020
感谢您的后续问题 - 我认为该参数实际上更适合日志记录,关键字参数比将回溯放入字符串的自定义代码更易于维护。我会更新我的答案。exc_info=True
10赞 Edward Newell 11/16/2015 #7

要获得精确的堆栈跟踪,作为一个字符串,如果没有 try/except 单步执行它,就会引发它,只需将其放在捕获有问题的异常的 except 块中即可。

desired_trace = traceback.format_exc(sys.exc_info())

以下是如何使用它(假设已定义,并调用您喜欢的日志记录系统):flaky_funclog

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

捕获并重新引发 s 是个好主意,这样您仍然可以使用 Ctrl-C 终止程序。 日志记录超出了问题的范围,但一个不错的选择是日志记录systraceback 模块的文档。KeyboardInterrupt

评论

6赞 martineau 2/7/2018
这在 Python 3 中不起作用,需要更改为 .作为参数传递从来都不是正确的做法,但在 Python 2 中被默默地忽略了——但在 Python 3(无论如何是 3.6.4)中却没有被忽略。desired_trace = traceback.format_exc()sys.exc_info()
5赞 AJNeufeld 8/18/2018
KeyboardInterrupt不是(直接或间接)派生自 。(两者都派生自 。这意味着永远不会抓住一个,因此是完全没有必要的。ExceptionBaseExceptionexcept Exception:KeyboardInterruptexcept KeyboardInterrupt: raise
1赞 Nam G VU 3/12/2020
traceback.format_exc(sys.exc_info())Python 3.6.10 对我不起作用
27赞 Mark McDonald 4/24/2018 #8

除了 Aaron Hall 的回答之外,如果您正在记录,但不想使用(因为它以 ERROR 级别记录),您可以使用较低级别并通过 .例如:logging.exception()exc_info=True

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)

评论

0赞 mike rodent 12/22/2021
在处理检测到的日志记录故障时,这也很好......即,由于某种原因您未能创建实际的 Logger 对象。
7赞 Basj 11/7/2018 #9

关于这个答案的评论的评论:对我来说比 .对于后者,有时会奇怪地与回溯文本“混合”,例如如果两者都想同时写入 stdout 或 stderr,则会产生奇怪的输出(至少在从文本编辑器内部构建并在“构建结果”面板中查看输出时)。print(traceback.format_exc())traceback.print_exc()hello

回溯(最近一次调用最后一次):
文件“C:\Users\User\Desktop\test.py”,第 7 行,在
地狱 do_stuff()
文件“C:\Users\User\Desktop\test.py”,第 4 行,在
do_stuff 1/0 中 ZeroDivisionError:整数除法或取模为零
o
[在 0.1
秒内完成]

所以我使用:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
51赞 Anthony 12/14/2018 #10

首先,不要使用 s 进行日志记录,有一个稳定的、经过验证的、经过深思熟虑的模块可以做到这一点:日志记录。你绝对应该使用它来代替。printstdlib

其次,当有一种原生的简单方法时,不要试图用不相关的工具搞。在这里:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

就是这样。你现在完成了。

对任何对引擎盖下事物如何运作感兴趣的人的解释

实际做的只是调用(即,使用级别记录事件)并打印回溯。log.exceptionlog.errorERROR

为什么更好?

好吧,这里有一些注意事项:

  • 到好处;
  • 它很简单;
  • 这很简单。

为什么没有人应该使用或调用记录器或弄脏他们的手?tracebackexc_info=Truesys.exc_info

好吧,只是因为!它们都出于不同的目的而存在。例如,的输出与解释器本身生成的回溯略有不同。如果你使用它,你会让任何阅读你的日志的人感到困惑,他们会用头撞他们。traceback.print_exc

传递到日志调用是不合适的。但是,它在捕获可恢复的错误时很有用,并且您还希望使用回溯来记录它们(例如使用级别),因为仅生成一个级别的日志 - 。exc_info=TrueINFOlog.exceptionERROR

而且你绝对应该尽可能多地避免惹恼。它不是一个公共接口,而是一个内部接口——如果你肯定知道自己在做什么,你可以使用它。它不仅适用于打印异常。sys.exc_info

评论

14赞 A. Rager 4/28/2019
它也不能按原样工作。不是这样。我现在还没有完成:这个答案只是浪费时间。
0赞 Shital Shah 5/18/2019
我还想补充一点,你可以做.除非有特殊要求,否则无需创建日志实例。logging.exception()
17赞 Break 12/18/2020
我觉得这个答案有点荒谬。它充满了“做/不要这样做只是因为”,而没有解释为什么。你在“为什么更好?”中的观点实际上都在说同一件事:“因为它是。我觉得这没有帮助。你至少解释了一点。
1赞 rjh 9/5/2021
很好的信息(我不知道),但有点居高临下。我认为这是由于语言障碍而不是恶意。logging.exception
0赞 omni 1/14/2022
这家伙说了什么。在我的公司里,我们会解雇任何使用打印进行日志记录的人。/秒
46赞 Ciro Santilli OurBigBook.com 5/18/2019 #11

traceback.format_exception(exception_object)

如果只有异常对象,则可以使用以下命令从 Python 3 中代码的任何点获取字符串形式的回溯:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

从 Python 3.10 开始,这可以进一步简化为: 谢谢

tb_str = ''.join(traceback.format_exception(exc_obj))

完整示例:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc_obj = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

或使用更简单的 Python 3.10 界面:

tb_str = ''.join(traceback.format_exception(exc_obj))

其中任何一个的输出:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

文档:https://docs.python.org/3.10/library/traceback.html#traceback.format_exception

另请参阅:从异常对象中提取回溯信息

测试 Python 3.11.4, Ubuntu 23.04.

评论

8赞 Jack M 5/19/2022
为什么在语言中做到这一点的唯一方法涉及两次传递相同的信息(两个和),以及一个不相关的第三个参数?exc_objexc_obj.__traceback__None
0赞 matanster 7/7/2023
我认为这只有在您提出然后捕获异常时才有效,但如果您尝试在提出您创建的异常对象之前获取回溯,则不起作用,您可能希望在某些设计中这样做。
26赞 bgdnlp 6/1/2019 #12

我在任何其他答案中都没有看到这一点。如果出于某种原因传递 Exception 对象...

在 Python 3.5+ 中,您可以使用 traceback 从 Exception 对象获取跟踪。TracebackException.from_exception()中。例如:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

但是,上述代码会导致:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

这只是堆栈的两层,而不是如果异常被提出并且没有被截获(取消注释该行)在屏幕上打印的内容。stack_lvl_2()# raise

据我了解,这是因为在这种情况下,异常在引发时仅记录堆栈的当前级别。当它通过堆栈向上传递时,更多的级别被添加到其 .但是我们截获了它,这意味着它所记录的只是3级和2级。为了获得在 stdout 上打印的完整跟踪,我们必须在最高(最低?)级别捕获它:stack_lvl_3()__traceback__stack_lvl_2()

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

其结果是:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

请注意,堆栈打印不同,第一行和最后一行缺失。因为它是不同的格式()。

在尽可能远离异常引发点的地方截获异常可以使代码更简单,同时提供更多信息。

评论

0赞 elhefe 2/21/2020
这比以前的方法要好得多,但仅仅为了打印堆栈跟踪,仍然非常复杂。Java 占用的代码 FGS 更少。
0赞 barjak 3/28/2023
另一种选择是 。print(''.join(tb1.format()))sys.stdout.writelines(tb1.format())
0赞 matanster 7/7/2023
但是,如果您尝试在引发异常之前访问它,则跟踪信息将为空
15赞 nupanick 11/26/2019 #13

如果你已经有一个 Error 对象,并且想要打印整个内容,则需要进行这个有点尴尬的调用:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

没错,它有三个位置参数:异常的类型、实际的异常对象和异常自己的内部回溯属性。print_exception

在 python 3.5 或更高版本中,是可选的...但这是一个位置参数,因此您仍然必须显式地将 None 传递到它的位置。type(err)

traceback.print_exception(None, err, err.__traceback__)

我不知道为什么这一切不仅仅是.为什么你要打印出一个错误,以及一个不属于该错误的回溯,这超出了我的范围。traceback.print_exception(err)

2赞 Ahmad 10/10/2020 #14

这是我将错误写入日志文件和控制台的解决方案:

import logging, sys
import traceback
logging.basicConfig(filename='error.log', level=logging.DEBUG)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    exc_info=(exc_type, exc_value, exc_traceback)
    logging.critical("\nDate:" + str(datetime.datetime.now()), exc_info=(exc_type, exc_value, exc_traceback))
    print("An error occured, check error.log to see the error details")
    traceback.print_exception(*exc_info)


sys.excepthook = handle_exception
3赞 Bar Nuri 10/16/2020 #15

Python 3 解决方案

stacktrace_helper.py:

from linecache import getline
import sys
import traceback


def get_stack_trace():
    exc_type, exc_value, exc_tb = sys.exc_info()
    trace = traceback.format_stack()
    trace = list(filter(lambda x: ("\\lib\\" not in x and "/lib/" not in x and "stacktrace_helper.py" not in x), trace))
    ex_type = exc_type.__name__
    ex_line = exc_tb.tb_lineno
    ex_file = exc_tb.tb_frame.f_code.co_filename
    ex_message = str(exc_value)
    line_code = ""
    try:
        line_code = getline(ex_file, ex_line).strip()
    except:
        pass

    trace.insert(
        0, f'File "{ex_file}", line {ex_line}, line_code: {line_code} , ex: {ex_type} {ex_message}',
    )
    return trace


def get_stack_trace_str(msg: str = ""):
    trace = list(get_stack_trace())
    trace_str = "\n".join(list(map(str, trace)))
    trace_str = msg + "\n" + trace_str
    return trace_str
-7赞 A.I. 1/21/2021 #16

您可以执行以下操作:

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    raise err
23赞 soumya-kole 10/3/2021 #17

在 python3(适用于 3.9)中,我们可以定义一个函数,并且可以在我们想要打印详细信息的任何地方使用它。

import traceback

def get_traceback(e):
    lines = traceback.format_exception(type(e), e, e.__traceback__)
    return ''.join(lines)

try:
    1/0
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

try:
    spam(1,2)
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

输出如下所示:

bash-3.2$ python3 /Users/soumyabratakole/PycharmProjects/pythonProject/main.py
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 26, in <module>
    1/0
ZeroDivisionError: division by zero

------End--------
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 33, in <module>
    spam(1,2)
NameError: name 'spam' is not defined

------End--------
9赞 Alexander Serkin 11/19/2021 #18
import io
import traceback

try:
    call_code_that_fails()
except:

    errors = io.StringIO()
    traceback.print_exc(file=errors)  # Instead of printing directly to stdout, the result can be further processed
    contents = str(errors.getvalue())
    print(contents)
    errors.close()

评论

6赞 joanis 11/20/2021
2 条评论:在之前的回答中已经讨论过 的用法。更重要的是,当最后五行完全等于时,为什么要胡说八道?traceback.print_exc()io.StringIOtraceback.print_exc()
4赞 pcko1 4/6/2022
@joanis 我相信,如果您想访问错误正文而不仅仅是打印它,这些行很有用。就我个人而言,我发现它很有用,因为我正在将堆栈跟踪记录到数据库。
0赞 Akshay Hazari 8/3/2023
@joanis 这正是我所需要的,将回溯发送到日志。尽管其他人已经使用了format_exception功能,但这很有帮助,并且会使用它。但很高兴知道这一点,因为在这种情况下使用它是通用的,其中输出直接进入 stdout。他使用 file 选项将错误发送到 errors 变量并捕获它。
3赞 Asclepius 5/17/2023 #19

此答案中的这种用法是自 Python 3.10 以来的新用法,并且之前的任何答案都没有涉及。要打印回溯,可以将异常提供给traceback.print_exception

例:

import traceback

try:
    object.bad_attr
except Exception as exc:
    traceback.print_exception(exc)

输出回溯:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: type object 'object' has no attribute 'bad_attr'

若要将跟踪捕获为字符串,请参阅此答案

评论

0赞 intergallactic 6/9/2023
它不起作用
1赞 Asclepius 6/9/2023
@intergallactic 请尝试答案中的示例,因为它已经过测试。
1赞 Andrew 7/20/2023
我可以确认这确实有效,并且比 Alexander Serkin 的答案更简单、更容易实现。