调试Python代码的指南(以及为什么要学习)
#编程 #生产率 #python #调试

每当您开始为应用程序编写代码时,从第一次尝试开始就可以使用一切,您永远不会遇到异常和错误,用户很高兴,您也是如此。这会很棒,不会吗?不幸的是,只有在梦中才有可能。现实是您的代码失败。它提出了例外,会产生意想不到的结果,使用户不开心。

这就是为什么调试技能在开发人员工具包中必不可少的原因。调试是确定和修复代码中错误的过程。尽管可以用眼睛发现基本错误,但随着应用程序变得越来越复杂,我们需要工具来提供帮助。本文讨论了这些工具的重要性以及您为什么现在需要掌握它们的原因。

谁是调试?

Image description
应用程序问题通常分为两类:提高例外情况并默默地做错事。有时,例外是明确的,立即揭示了错误。但是,对于复杂的情况,答案不在表面上,无论是一个奇怪的例外,还是遇到程序运行的绝对意外结果。这就是我们需要调试的地方。

有几种修复代码的选项。最简单的方法是混乱地改变它的不同部分,希望最好。有时候它有帮助,但是说实话,这太效率了,可能会浪费您的时间。

有效地进行更改,了解问题,因此,它可能的来源是关键。我们从研究追溯开始。在这里,当您了解问题时,您可能会开始盯着您的代码,试图考虑解决方案。也许您会在这里和那里添加Print(),也许他们甚至会给您一个线索。当您的应用程序简单时,这种方法是合法的,但是,令人惊讶的是,即使经过多年的工作,许多开发人员也坚持使用,花了越来越多的时间来越来越复杂。

第三种选择是前两个进化的顶部:适当的调试作为工具,方法和经验的结合。

调试和工具类型的目标

调试的最终目标是发现问题,并弄清每条代码如何影响损坏的流,每个值如何随着程序的运行而变化。在此之后,唯一剩下的就是实施更好的解决方案,以涵盖边缘案例。

调试涉及设置断点,逐步浏览代码和检查值。现有的调试工具可以分为三种主要类型:

  • 独立的UI应用程序 - 例如,PUDB,WinPDB。这些是专门为调试设计的应用程序,并且UI基本上与IDE相同,因此,我认为这不值得浪费您的时间。但是,如果您感兴趣的话,您总是可以自己了解更多;
  • 代码中的工具,您可以通过在程序中添加临时代码来设置断点;
  • ides。所有流行的现代开发环境,例如Pycharm,Visual Studio,Pydev等,都集成了调试工具,使您可以同时运行和检查代码。

现在让我们更多地谈论最后两个点。

一个例子

要解释如何在行动中进行调试,我将通过下面的示例代码向您展示该过程。

让您说您正在创建一个使用某些外部天气API的应用程序。您可以构建一个pydantic模型,以加载其在其之后使用数据(例如,将其打印到控制台)中加载API响应。看看这个示例。此代码不起作用。

from pydantic import BaseModel

class ApiResponse(BaseModel):
    city_id: int
    temperature: float
    weather: str

# we don’t see the contents of this method, pretend this is what an actual HTTP API call returns
def fake_weather_api_request(city: str):
    cities = {"New York": {"id": 42, "temperature": 25.6, "weather_type": "sunny"},
              "Marrakech": {"id": 12, "temperature": 30}}
    return cities.get(city)

if __name__ == "__main__":
    weather_resp = fake_weather_api_request("New York")
    ny_weather = ApiResponse(**weather_resp)
    print(f"Weather in New York (city id={ny_weather.city_id}): \n" +
          f"Temperature: {ny_weather.temperature} \n" +
          f"Weather type: {ny_weather.weather}")

运行它时,您会看到以下异常消息:

Image description

哦。那意味着什么?如果我们采取简单的方法并试图猜测问题,我们可能会认为 - 可能,以下领域不会对某些城市产生回应。让我们让它们无效!

class ApiResponse(BaseModel):
    city_id: int = None
    temperature: float = None
    weather: str = None

很棒。它不再失败了。

Image description

还是?如果我们确定API同时拥有纽约的城市ID和天气类型信息怎么办?那为什么不呢?我们可以继续玩猜测游戏,但这实际上是开始调试的最佳时刻。

Python调试器

Python为您提供了一个很棒的内置代码调试器: * pdb *模块。这是一个易于使用的交互式Python代码调试器。在3.6之前的Python版本中,我们需要在文件开头导入模块以使用调试器。但是,在现代python中,即使不再需要,只要您希望您的程序暂停就可以使用。

让我们在API调用之前设置一个断点。

if __name__ == "__main__":
    breakpoint()
    weather_resp = fake_weather_api_request("New York")
    ny_weather = ApiResponse(**weather_resp)

现在您可以从终端运行该应用程序:

Image description

应用程序执行已在您设置断点的地方暂停,并且PDB现在正在等待您的命令输入。键入 h 查看可用命令的列表:

Image description

现在,您可以在程序执行的不同阶段浏览代码,打印变量值,进入方法,查看内部发生的事情,等等。让我们使用命令 n (下一个),然后查看 p

的实际响应体是什么

Image description

好吧,事实证明,响应实际上包含所有三个字段,但是为什么在打印出来时将其中一些显示为None呢?如果您仔细查看文件开头定义的ApiResponse模型,您会发现某些字段名称有些不正确: city_id 而不是 id 和<强>天气而不是 weather_type 。实际上,实际的API响应确实不包含这些字段。让我们修复这个。

class ApiResponse(BaseModel):
    id: int = None
    temperature: float = None
    weather_type: str = None

和不忘记在打印语句中更改字段名称,然后删除/评论breakpoint()呼叫:

if __name__ == "__main__":
    #breakpoint()
    weather_resp = fake_weather_api_request("New York")
    ny_weather = ApiResponse(**weather_resp)
    print(f"Weather in New York (city id={ny_weather.id}): \n" +
          f"Temperature: {ny_weather.temperature} \n" +
          f"Weather type: {ny_weather.weather_type}")

再次运行代码后,它可以工作!

Image description

恭喜,您刚刚成功完成了第一轮调试。 :)

在IDE中调试

IDE(集成开发环境)可帮助您有效地开发软件。它结合了软件开发所需的所有主要功能 - 代码编辑,构建,运行,测试,最后是调试。 IDE使调试变得非常容易,因此除非您是终端的僵化粉丝,否则我建议尝试在IDE中进行调试。

大多数IDE都以类似的原则运行,并且审查所有这些原则都不是有意义的。因此,我将在我首选的IDE中向您展示一些示例,但是您可以在您选择的任何其他开发环境中都可以轻松地做同样的事情。

让我们在Pycharm中打开相同的程序代码。要设置断点,您需要单击要暂停的行左侧。

Image description

这将在接收API响应后停止执行程序。要查看它的作用,请点击窗户右上角的绿色错误按钮。

Image description

当应用程序执行流达到标记的代码线时,Pycharm将打开一个包含有用内容的调试子窗口。

Image description

上图上(1)是当前定义的变量及其值的列表。每个变量都可以在所有级别上进行扩展和深入检查。当变量值是具有不同类嵌套值的复杂对象时,这将非常有用。 (2)是最新的接收/更改值。这对于快速检查很有帮助,如果该值像我们的情况或字符串一样简单,那么该值很简单。最后但并非最不重要的一点是,在(3)上,您可以看到用于踏入代码的工具箱。他们的描述以及有关Pycharm中调试的一些其他文档可以在官方网站上找到:https://www.jetbrains.com/pycharm/features/debugger.html

但是,

i希望额外注意所有踏脚按钮旁边的微小计算器按钮。此按钮称为评估表达式,名称是不言自明的。当您希望看到表达式不同的情况下而不更改和重新运行代码时,这将为您节省很多时间。在我们的天气API示例中,我们可以使用该工具来查看纽约以外的城市的反应。

Image description

瞧,我们可以看到,对于某些城市,例如Marrakech,Weather_Type属性缺少。我们还可以尝试立即将响应加载到Apiresponse模型中,并查看其表现。

Image description

事实证明,对于Marrakech,weather_type字段将为None

如果您尝试评估不正确的表达式,Pycharm将向您显示错误。但是,只有在评估窗口中可见此错误,并且不会打破程序流。在检查了所有想要的一切之后,您可以像尝试这些事情一样关闭窗口并继续浏览代码。

Image description


只有通过经验才能获得调试技能,但是一旦您接受了调试技能,您将会感到惊讶,因为您没有长时间的工作。犯错是绝对自然的,编写和编写无漏洞的代码几乎是不可能的(如果您每年编写多行超过几行)。真正重要的是您调查问题并解决问题的能力。


您发现这篇文章有帮助吗?点击并关注以后再阅读更多内容:)