Python内置(2)异常、常量、globals

2022-12-06 14:29:46 浏览数 (1)

异常

Python有66个内置的异常(exception)类,每个类都旨在供用户,标准库和其他所有人使用,作为解释和捕获代码中错误的有意义的方法。

为了确切解释为什么Python中有单独的异常类,这里有一个快速示例:

代码语言:javascript复制
def fetch_from_cache(key):
    """Returns a key's value from cached items."""
    if key is None:
        raise ValueError('key must not be None')

    return cached_items[key]

def get_value(key):
    try:
        value = fetch_from_cache(key)
    except KeyError:
        value = fetch_from_api(key)

    return values

专注于函数get_value。如果key存在,它应该返回一个缓存值,否则从API获取数据。

该函数中可能发生 3 件事:

  • • 如果key不在缓存中,则尝试访问cached_items[key]将引发一个KeyError .这会在try块中捕获,并进行 API 调用以获取数据。
  • • 如果key存在于缓存中,则按原样返回。
  • • 还有第三种情况,其中keyNone

如果键是None ,则fetch_from_cache引发 一个ValueError ,指示提供给此函数的值不合适。由于该try块只捕获 KeyError,因此此错误直接显示给用户。

如果没有预定义ValueErrorKeyError,就不能这样区分错误类型。

关于异常的更多内容,如异常的子类化,Exception几乎是任何异常的父类、BaseException是所有异常的父类。这里不在赘述。

现在我应该指出,上面输出中的所有大写值都不是异常类型,实际上,Python中有另一种类型的内置对象是大写的:常量。让我们来谈谈这些。

常量

有5种内置的常量(constants):True, False, None, Ellipsis,和NotImplemented。

Ture, False 和 None 出现的很多。Ellipsis(省略号)很有意思,它有两种形式:Ellipsis...。最常出现在类型注解(annotations)和一些切片操作中。 NotImplemented用于类内的运算符(operator)定义,当你想要告诉Python类的运算符还没有具体定义。

Python中的对象可以通过实现__add__实现对 运算符的支持。__iadd__ =提供支持。等等。例如:

代码语言:javascript复制
class MyNumber:
    def __add__(self, other):
        return other   42
代码语言:javascript复制
>>> num = MyNumber()
>>> num   3
45
>>> num   100
142

补充 右运算符。上面实现的__add__ 只有对象在运算符左侧才有效。 __radd__是右运算符,添加后就可以计算3 num

如果你想要只在整数进行加法,而不包括浮点数,这是就用到了NotImplemented

代码语言:javascript复制
class MyNumber:
    def __add__(self, other):
        if isinstance(other, float):
            return NotImplemented

        return other   42

关于常量的一个奇怪的事实是,它们甚至不是在Python中实现的,而是直接在C代码中实现的。

globals

内置的输出有一些奇怪的东西,例如__spec__,__loader____debug__等。

这些实际上不是builtins模块所独有的。这些属性都存在于Python中每个模块的全局范围内,因为它们是模块属性。它们保存有关导入所需的模块的信息。让我们来看看它们: __name__ 包含模块的名称。例如builtins.__name__的值是字符串'builtins'。当你运行一个Python文件,也是在运行一个模块,此时该模块的名称为__main__。这就结束了为什么 if __name__ == '__main__'内的语句会被执行。

__doc__ 包含模块的文档字符串。这是执行help(module_name)时显示为模块说明的内容。

__package__ 此模块所属的包。对于顶级模块,它与__name__相同。对于子模块,它是包的__name__ 。例如:

代码语言:javascript复制
>>> import urllib.request
>>> urllib.__package__
'urllib'
>>> urllib.request.__name__
'urllib.request'
>>> urllib.request.__package__
'urllib'

__spec__ 这是指模块的空间。它包含元数据,例如模块名称,它是哪种模块,以及它的创建和加载方式。

__loader__ __loader__设置为导入在加载模块时使用的加载程序对象。这个特定的模块在_frozen_importlib模块中定义,并且是用于导入内置模块的内容。

__import__ __import__是定义import语句在 Python 中的工作方式的内置函数。

代码语言:javascript复制
np = __import__('numpy')  # Same as doing 'import numpy as np'

__debug__ 这是 Python 中的一个全局常量值,几乎总是设置为 True。它指的是Python在调试模式下运行。默认情况下,Python始终在调试模式下运行。

此外,__debug__, True, FalseNone是 Python 中唯一的真常量,即这 4 个是 Python 中唯一不能用新值覆盖的全局变量。

__build_class__ 此全局变量是在 Python 3.1 中添加的,以允许类定义接受任意位置和关键字参数。为什么这是一个功能有很长的技术原因,它涉及元类等高级主题,所以不幸的是,我不会解释为什么它存在。

__cached__ 导入__cached__模块时,该属性存储该模块的已编译 Python 字节码的缓存文件的路径。你可能会惊讶,Python也要编译吗?是的。Python被编译。事实上,所有的Python代码都是被编译的,但不是机器代码 ,而是字节码(bytecode)。让我通过解释Python如何运行你的代码来解释这一点。

  1. 1. 获取源文件,并解析为语法树。保证语法正确。
  2. 2. 将语法树编译为字节码。字节码是Python虚拟机(virtual machine,VM)的一组微指令。这个“虚拟机”是Python的解释器逻辑所在的位置。它本质上是在您的机器上模拟一个非常简单的基于堆栈的计算机,以便执行您编写的Python代码。
  3. 3. 然后,在 Python VM 上运行此代码形式的代码。字节码指令很简单,例如从当前堆栈中推送和弹出数据。当这些指令一个接一个地运行时,这些指令中的每一个都会执行整个程序。

现在,由于上面的“编译为字节码”步骤在导入模块时需要花费大量时间,因此Python将字节码存储(编组)到.pyc文件中,并将其存储在名为__pycache__的文件夹中。然后,导入模块的__cached__参数指向此.pyc文件。你可以直接在Python代码中运行或导入一个.pyc文件,就像运行一个.py文件。

0 人点赞