Python 3.11新功能:错误信息回溯

错误信息回溯

长按关注《Python学研大本营》,加入读者群,分享更多精彩 扫码关注《Python学研大本营》,加入读者群,分享更多精彩

Python 3.11于2022 年 10 月 24 日发布。这个最新版本的 Python 速度更快,对用户更友好。

与每个版本一样,Python 3.11 都进行了许多改进和更改。您可以在文档中看到所有这些的列表。在这里,您将探索最酷、最具影响力的新功能。

在本专题的教程中,您将了解新功能和改进,例如:

  • 更好的错误消息和更多信息的回溯

  • 由于Faster CPython项目付出了巨大的努力,代码执行速度更快

  • 简化使用异步代码的任务和异常组

  • 改进 Python 的静态类型支持的几个新类型特性

  • 本机TOML 支持处理配置文件

如果您想尝试本教程中的任何示例,则需要使用 Python 3.11。Python 3 安装和设置指南(https://realpython.com/installing-python/)以及如何安装 Python 的预发布版本(https://realpython.com/python-pre-release/)引导您完成向系统添加新版本 Python 的几个选项。

除了了解有关该语言的新功能的更多信息外,您还将获得一些关于在升级到新版本之前要考虑什么的建议。下面的链接下载演示 Python 3.11 新功能的代码示例(https://realpython.com/bonus/python-311-examples/)

信息错误回溯

Python 通常被认为是一种很好的初学者编程语言,它具有可读的语法和强大的数据结构。所有人都面临的挑战,尤其是那些刚接触 Python 的人,是如何解释Python 遇到错误时显示的回溯。

在Python 3.10中,Python 的错误信息得到了极大的改进。同样,Python 3.11最受期待的功能之一也将提升您的开发者体验。装饰性注释被添加到回溯中,可以帮助您更快地解释错误消息。

要查看增强回溯的快速示例,请将以下代码添加到inverse.py的文件中:

# inverse.py

def inverse(number):
    return 1 / number

print(inverse(0))

你可以用它inverse()来计算一个数的乘法倒数。没有乘法逆元0,因此您的代码在运行时会引发错误:

$ python inverse.py
Traceback (most recent call last):
  File "/home/realpython/inverse.py", line 6, in <module>
    print(inverse(0))
          ^^^^^^^^^^
  File "/home/realpython/inverse.py", line 4, in inverse
    return 1 / number
           ~~^~~~~~~~
ZeroDivisionError: division by zero

请注意嵌入在回溯中的^和符号~,它们用于引导您注意导致错误的代码。像往常一样使用回溯,您应该从底部开始,然后逐步向上。在此示例中, aZeroDivisionError是由除法引起的1 / number。真正的罪魁祸首是inverse(0),因为0没有相反的情况。

在发现错误时获得这种额外的帮助很有用。但是,如果您的代码更复杂,带注释的回溯功能会更加强大。他们可能能够传达您以前无法从回溯中获得的信息。

要了解改进的回溯的强大功能,您将构建一个关于少数程序员的信息的小型解析器。假设您有一个名为programmers.json的文件:

[
    {"name": {"first": "Uncle Barry"}},
    {
        "name": {"first": "Ada", "last": "Lovelace"},
        "birth": {"year": 1815},
        "death": {"month": 11, "day": 27}
    },
    {
        "name": {"first": "Grace", "last": "Hopper"},
        "birth": {"year": 1906, "month": 12, "day": 9},
        "death": {"year": 1992, "month": 1, "day": 1}
    },
    {
        "name": {"first": "Ole-Johan", "last": "Dahl"},
        "birth": {"year": 1931, "month": 10, "day": 12},
        "death": {"year": 2002, "month": 6, "day": 29}
    },
    {
        "name": {"first": "Guido", "last": "Van Rossum"},
        "birth": {"year": 1956, "month": 1, "day": 31},
        "death": null
    }
]

请注意,有关程序员的信息非常不一致。虽然有关Grace Hopper和Ole-Johan Dahl的信息已完成,但您会错过Ada Lovelace 的出生日期和月份以及她的死亡年份。自然,您只有Guido van Rossum的出生信息。最重要的是,您只记录了Barry 叔叔的名字。

您将创建一个可以包装此信息的类。首先从 JSON 文件中读取信息:

# programmers.py

import json
import pathlib

programmers = json.loads(
    pathlib.Path("programmers.json").read_text(encoding="utf-8")
)

您用于pathlib读取 JSON 文件并将json信息解析为 Python 字典列表。

接下来,您将使用数据类来封装有关每个程序员的信息:

# programmers.py

from dataclasses import dataclass

# ...

@dataclass
class Person:
    name: str
    life_span: tuple[int, int]

    @classmethod
    def from_dict(cls, info):
        return cls(
            name=f"{info['name']['first']} {info['name']['last']}",
            life_span=(info["birth"]["year"], info["death"]["year"]),
        )

每个Person都有一个name和一个life_span属性。此外,您还可以添加一个方便的构造函数,该构造函数Person可以根据 JSON 文件中的信息和结构进行初始化。

您还将添加一个可以一次性初始化两个Person对象的函数:

# programmers.py

# ...

def convert_pair(first, second):
    return Person.from_dict(first), Person.from_dict(second)

该convert_pair()函数两次使用.from_dict()构造函数将一对程序员从 JSON 结构转换为Person对象。

是时候探索您的代码了,尤其是查看一些回溯。使用标志-i运行程序以打开 Python 的交互式 REPL,其中包含所有可用的变量、类和函数:

$ python -i programmers.py
>>> Person.from_dict(programmers[2])
Person(name='Grace Hopper', life_span=(1906, 1992))

Grace 的信息是完整的,因此您可以将她封装到一个Person包含有关她的全名和寿命信息的对象中。

要查看实际的新回溯,请尝试转换 Barry 叔叔:

>>> programmers[0]
{'name': {'first': 'Uncle Barry'}}

>>> Person.from_dict(programmers[0])
Traceback (most recent call last):
  File "/home/realpython/programmers.py", line 17, in from_dict
    name=f"{info['name']['first']} {info['name']['last']}",
                                    ~~~~~~~~~~~~^^^^^^^^
KeyError: 'last'

你得到一个KeyError因为last缺少。虽然您可能记得那last是name中的一个子字,但注释会立即为您指出这一点。

同样,回想一下关于 Ada 的生命周期信息是不完整的。你不能为她创建一个Person对象:

>>> programmers[1]
{
    'name': {'first': 'Ada', 'last': 'Lovelace'},
    'birth': {'year': 1815},
    'death': {'month': 11, 'day': 27}
}

>>> Person.from_dict(programmers[1])
Traceback (most recent call last):
  File "/home/realpython/programmers.py", line 18, in from_dict
    life_span=(info["birth"]["year"], info["death"]["year"]),
                                      ~~~~~~~~~~~~~^^^^^^^^
KeyError: 'year'

你得到了另一个KeyError,这次是因为year失踪了。在这种情况下,回溯比前面的例子更有用。您有两个year子字段,一个 forbirth和一个 for death。回溯注释立即显示您错过了死亡年份。

Guido怎么样了?你只有关于他出生的信息:

>>> programmers[4]
{
    'name': {'first': 'Guido', 'last': 'Van Rossum'},
    'birth': {'year': 1956, 'month': 1, 'day': 31},
    'death': None
}

>>> Person.from_dict(programmers[4])
Traceback (most recent call last):
  File "/home/realpython/programmers.py", line 18, in from_dict
    life_span=(info["birth"]["year"], info["death"]["year"]),
                                      ~~~~~~~~~~~~~^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

在这种情况下,TypeError出现了。您之前可能已经看到过这些'NoneType'类型的错误。众所周知,它们很难调试,因为不清楚哪个对象是出乎意料的None。但是,从注释中,您会看到info["death"]在此示例中是None。

在最后一个示例中,您将探索嵌套函数调用会发生什么。请记住,convert_pair()调用Person.from_dict()两次。现在,尝试将 Ada 和 Ole-Johan 配对:

>>> convert_pair(programmers[3], programmers[1])
Traceback (most recent call last):
  File "/home/realpython/programmers.py", line 24, in convert_pair
    return Person.from_dict(first), Person.from_dict(second)
                                    ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/realpython/programmers.py", line 18, in from_dict
    life_span=(info["birth"]["year"], info["death"]["year"]),
                                      ~~~~~~~~~~~~~^^^^^^^^
KeyError: 'year'

尝试封装 Ada 引发与KeyError之前相同的情况。但是,请注意来自内部的回溯convert_pair()。因为该函数调用两次.from_dict(),所以通常需要一些努力才能确定在处理first或second时是否引发了错误。在最新版本的 Python 中,您会立即看到问题是由second引起的.

这些回溯使 Python 3.11 中的调试比早期版本更容易。您可以在 Python 3.11 预览教程Even Better Error Messages(https://realpython.com/python311-error-messages/)中查看更多示例、有关如何实现回溯的更多信息以及可以在调试中使用的其他工具。有关更多技术细节,请查看PEP 657(https://peps.python.org/pep-0657/)。

带注释的回溯将有助于提高 Python 开发人员的工作效率。

推荐书单

《Pandas1.x实例精解》

本书详细阐述了与Pandas相关的基本解决方案,主要包括Pandas基础,DataFrame基本操作,创建和保留DataFrame,开始数据分析,探索性数据分析,选择数据子集,过滤行,对齐索引,分组以进行聚合、过滤和转换,将数据重组为规整形式,组合Pandas对象,时间序列分析,使用Matplotlib、Pandas和Seaborn进行可视化,调试和测试等内容。此外,本书还提供了相应的示例、代码,以帮助读者进一步理解相关方案的实现过程。 本书适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学用书和参考手册。

链接:https://u.jd.com/UKjx4et

精彩回顾

《Pandas1.x实例精解》新书抢先看!

【第1篇】利用Pandas操作DataFrame的列与行

【第2篇】Pandas如何对DataFrame排序和统计

【第3篇】Pandas如何使用DataFrame方法链

【第4篇】Pandas如何比较缺失值以及转置方向?

【第5篇】DataFrame如何玩转多样性数据

【第6篇】如何进行探索性数据分析?

【第7篇】使用Pandas处理分类数据

【第8篇】使用Pandas处理连续数据

【第9篇】使用Pandas比较连续值和连续列

【第10篇】如何比较分类值以及使用Pandas分析库

长按关注《Python学研大本营》

长按二维码,加入Python读者群

扫码关注《Python学研大本营》,加入读者群,分享更多精彩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值