当前位置: 首页 > news >正文

All In AI之三:一文构建Python核心语法体系

文章目录

  • 前言
  • 执行模型
    • 程序的结构
    • 名称与绑定
    • 命名空间和作用域
    • 名称的解析
    • local、nonlocal 和 global
  • 数据模型
    • 基类和元类
    • 内置元类
    • 抽象基类
    • 基础类型
      • 数字类型
      • 序列类型
        • 不可变序列
        • 可变序列
      • 集合类型
      • 映射类型
      • 可调用类型
      • 模块类型
      • 自定义类
      • 类实例
      • I/O 对象
    • 特殊方法名称
      • 基本定制
      • 自定义属性访问
      • 实现描述器
      • 调用描述器
    • 协程
  • 导入模型
    • 模块
      • 模块搜索路径
      • 标准模块
      • dir() 函数
      • 从包中导入 *
      • 多目录中的包

前言

所有编程语言都是为了作为人操作计算机资源的中介而存在,其中最重要的就是操作内存和CPU,其中操作内存的模式基本上就决定了这个编程语言的设计思想,所以快速且全面深入学习一个编程语言的方式就是从以下几个方面入手:

  • 执行模型
  • 数据模型
  • 导入模型

本文内容全部出自官方文档,且随着作者的不断实践会继续完善和修正。

执行模型

Python 程序的执行模型涉及到代码块、名称绑定、命名空间、作用域和名称解析等方面,理解这些概念对于写出高效、可维护的代码至关重要。以下是对这些概念的精简、整合和扩展,旨在使其更加全面且通俗易懂。

程序的结构

Python 程序由多个代码块组成,代码块是作为一个单元执行的一段 Python 程序文本。以下内容都属于代码块:

  • 模块
  • 函数体
  • 类定义
  • 交互式输入的每条命令
  • 通过脚本文件运行的程序
  • 使用 -c 选项在命令行中运行的命令
  • 使用 -m 参数运行的模块(即 __main__ 模块)
  • 传递给内置函数 eval()exec() 的字符串参数

每个代码块在执行时都会产生一个执行帧,帧中包含一些管理信息,如调试信息,并决定代码块执行完后如何继续。

名称与绑定

在 Python 中,名称是用来指代对象的。名称通过绑定操作被引入。绑定名称的操作包括:

  • 函数的正式参数
  • 类定义
  • 函数定义
  • 赋值表达式
  • import 语句
  • type 语句
  • del 语句(解除绑定)

如果某个名称在代码块内被绑定,它是该代码块的局部变量,除非它被声明为 nonlocalglobal。如果某个名称在模块层级被绑定,它则是全局变量。模块中的变量既是局部变量,也是全局变量。如果名称在某个代码块中被使用但没有绑定,它是自由变量。

命名空间和作用域

命名空间是名称到对象的映射,通常使用字典来实现。命名空间的例子包括:

  • 内置命名空间:存放 Python 内建函数和异常。
  • 模块的全局命名空间:每个模块的顶层变量和函数。
  • 局部命名空间:函数调用时创建的命名空间,包含函数内的变量。
  • 类的命名空间:类定义中的属性和方法。

命名空间的一个关键特点是,不同命名空间中的名称是相互独立的。命名空间在不同时间被创建,且拥有不同的生命周期。例如,内置名称的命名空间在 Python 解释器启动时创建,并且在整个程序运行期间都存在。模块的全局命名空间在读取模块定义时创建,并且通常会一直存在直到解释器退出。局部命名空间则是在函数调用时创建,在函数返回或异常抛出时销毁。递归调用会创建新的局部命名空间。

每个命名空间都有一个作用域,即它的可见范围,在该区域内可以直接访问该命名空间中的名称。作用域虽然是静态确定的,但会在运行时动态地使用。Python 在执行时通常会有三到四个嵌套的作用域:

  1. 最内层作用域,包含局部名称,优先在其中查找名称。
  2. 外层闭包作用域,包含“非局部、非全局”的名称,逐层向外搜索。
  3. 倒数第二层作用域,包含当前模块的全局名称。
  4. 最外层作用域,即内置名称的命名空间。

名称的解析

作用域定义了一个代码块中名称的可见性。当 Python 代码访问某个名称时,它会按照LEGB(Local、Enclosing、Global、Built-in)规则依次在作用域中查找:

  • Local:首先查找局部作用域。
  • Enclosing:如果没有找到,会查找外层函数的作用域。
  • Global:再查找模块的全局作用域。
  • Built-in:最后查找内置作用域。

如果在这些作用域中都没有找到该名称,会抛出 NameError 异常。如果在局部作用域中查找时发现名称未绑定,将抛出 UnboundLocalError 异常。

local、nonlocal 和 global

  • local:默认情况下,名称的绑定发生在局部作用域中,且只在当前代码块有效。
  • nonlocal:用于在嵌套函数中引用外层函数的变量,修改该变量时,不会在当前局部作用域创建新的绑定,而是直接修改外层作用域中的变量。
  • global:用于在函数或其他代码块中声明某个变量为全局变量,使得该变量指向模块的全局命名空间。修改时,会影响整个模块范围内的该变量。

数据模型

对象是Python中对数据的抽象。 Python程序中的所有数据都是由对象或对象间关系来表示的。每个对象有三个核心概念:

  • 标识号:标识号可以理解为该对象在内存中的地址, 一个对象被创建后它的标识号就绝不会改变, is运算符比较两个对象的标识号是否相同;id() 函数返回一个代表其标识号的整数。
  • 类型:对象的类型决定该对象所支持的操作并且定义了该类型的对象可能的取值。type() 函数能返回一个对象的类型(类型本身也是对象)。与标识号一样,一个对象的类型也是不可改变的。
  • 值:有些对象的值可以改变。值可以改变的对象被称为可变对象;值不可以改变的对象就被称为不可变对象。 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。

对象不会被显式地销毁,只有当无法访问时它们可能会被作为垃圾回收。

基类和元类

在Python中,object是所有类的顶级基类,而上文我们提到:类本身也是对象,那么将类看作一个对象时,用元类表示类的类,Python中的顶级元类就是type,即:

  • 所有类的基类最终链条都指向 object
  • 所有类的元类最终链条都指向 type
  • type 自己既是类又是元类,所以 type(type) = type

理解objecttype是关键,在学习Python数据模型的时候要以继承链为主而不是元类链条,这类似于Java中的类和Class的关系,只是Python设计的更加抽象。

内置元类

Python 内置的元类其实非常少,本质上 Python 里大部分类的元类都是 type,少数内置抽象基类使用 ABCMeta,还有一些标准库特殊类型使用自定义元类(如 EnumMeta):

object
type
ABCMeta
EnumMeta
元类作用使用对象
type默认元类,管理类的创建、决定类行为所有新式类(Python 3 中所有类)、object 本身
ABCMeta抽象基类元类,管理抽象方法、虚拟子类注册ABC 以及继承 ABC 或直接用 ABCMeta 的类,如 numbers.Numbercollections.abc.Sequence
EnumMeta枚举元类,控制 Enum 类的行为enum.Enum、用户自定义枚举类

为一个类定义确定适当的元类是根据以下规则:

  • 如果没有基类且没有显式指定元类,则使用type
  • 如果给出一个显式元类,则其会被直接用作元类,但如果不继承自type,则会报错;
  • 如果给出一个type的实例作为显式元类,或是定义了基类,则使用最近派生的元类。

抽象基类

在Python中有一种抽象基类的类比较特殊,它们的元类都是ABCMeta,可以用它们来定义接口规范或抽象方法,并且不能直接实例化,只能被子类继承或注册为虚拟子类。抽象基类类似于Java中的接口和抽象类,不同的是虚拟子类不是直接继承自ABCMeta类型的类,而是通过ABCMeta.register()注册的。

object
Number
Complex
Real
Rational
Integral
Iterable
Iterator
Container
Collection
Sequence
MutableSequence
Set
MutableSet
Mapping
MutableMapping
Callable
说明典型虚拟子类
Number所有数字类型int, float, complex
Complex复数complex
Real实数float, int
Rational有理数Fraction
Integral整数int
Iterable可迭代对象list, tuple, dict, str
Iterator迭代器文件对象、生成器
Sequence序列list, tuple, str
Mapping映射(字典)dict
Set集合set, frozenset
MutableSequence可变序列list
MutableMapping可变映射dict
MutableSet可变集合set
Callable可调用对象函数、方法、类

基础类型

object
NoneType
NotImplementedType
Ellipsis
int
bool
float
complex
bytes
bytearray
memoryview
range
str
list
dict
frozenset
set
type
类型布尔值是否可变说明
NoneTypeNonealse许多情况下它被用来表示空值,例如未显式指明返回值的函数将返回 None
NotImplementedTypeNotImplemented不应被解读为布尔值数值方法和丰富比较方法如未实现指定运算符表示的运算则应返回该值
EllipsisEllipsistrue此对象通过字面值 … 或内置名称 Ellipsis 访问

数字类型

在 Python 中,数字类型(如整数、浮点数和复数)是通过直接的字面量值(例如 13.142 + 3j)或通过运算生成的。Python 中的数字对象在创建时会自动根据其类型分配给相应的数字类。数字对象是不可变的。其中:

  • intIntegral 类的一个子类,用于表示任意大小的整数,支持大整数(不受大小限制,受内存限制)。
  • boolint 的子类,表示逻辑值 False(相当于 0)和 True(相当于 1)。它在 Python 中表现得像整数 01,除了在转换为字符串时,分别返回 "False""True"
  • floatReal 类的子类,用于表示双精度浮点数。
  • complexComplex的子类,用于表示复数,复数由实部和虚部组成,在 Python 中是由两个 float 数字表示的。
    • 复数的实部和虚部可以通过 .real.imag 属性获取。
    • 在 Python 中,复数表示为 a + bj,其中 a 为实部,b 为虚部,j 是虚数单位。
    • 复数支持常见的复数运算,如加减乘除等。

序列类型

在 Python 中,序列类型用于表示以非负整数为索引的有限有序集合。序列支持对元素的访问、切片操作等,并根据其可变性分为可变序列和不可变序列。

  • 序列的长度由 len() 函数返回,索引从 0 开始。
  • 支持负索引:a[-1] 返回序列中的最后一项,a[-2] 返回倒数第二项。
  • 支持切片:a[i:j] 返回索引从 ij-1 的所有项。
  • 支持扩展切片:a[i:j:k] 可以指定步长 k,返回每隔 k 项的切片。
不可变序列

不可变序列一旦创建后无法修改。虽然对象中可能包含对可变对象的引用,但这些对象本身不能修改。

  1. str(字符串):字符串是 Unicode 码位的序列,每个码位由一个字符表示。取值范围在 U+0000 到 U+10FFFF 之间。
    • 创建方式:使用' '" "''' '''""" """创建
    • 常用方法:
      • ord(c):返回字符 c 的 Unicode 码位。
      • chr(i):返回码位 i 对应的字符。
      • str.encode():将字符串编码为字节流(bytes)。
  2. tuple(元组):元组是不可变的序列,可以包含任意类型的对象。由多个元素构成,用逗号分隔。
    • 创建方式:使用()创建元组,单元素元组需要使用逗号来创建,例如 (1,)
  3. bytes(字节串):字节串是不可变的序列,包含 8 位字节,每个字节的取值范围为 0 至 255。
    • 创建方式:使用bytes()创建
    • 常用方法:
      • bytes.decode():将字节串解码为字符串。
可变序列

可变序列在创建后可以被修改,允许修改、删除元素,甚至改变其长度。

  1. list(列表):列表是可变的序列,可以包含任意类型的对象,支持动态修改。
    • 创建方式:通过[]创建
    • 常用操作:
      • append(x):在列表末尾添加元素 x
      • remove(x):删除列表中的元素 x
      • insert(i, x):在索引 i 处插入元素 x
      • pop(i):移除并返回索引 i 处的元素。
  2. bytearray(字节数组):字节数组是可变的字节序列,功能与字节串类似,但它可以被修改。
    • 创建方式:bytearray()
    • 常用方法:
      • bytearray.decode():将字节数组解码为字符串。
      • bytearray.append(x):在字节数组末尾添加字节 x

集合类型

集合类型是表示由不重复且不可变对象组成的无序有限集合。集合不支持通过下标来索引元素,但可以进行迭代。它们常用于高效的成员检测、去除重复项以及进行数学运算(如交集、并集、差集和对称差集等)。

  • 集合通过 len() 函数可以获取元素的数量。
  • 集合支持快速的成员检测(使用 in 关键字)。

其中:

  1. set(集合): 是一个可变的集合类型,支持添加和删除元素。
    • 创建方式:使用{}set() 函数创建,创建空集合只能使用set() 函数
    • 常用方法:
      • add(x):向集合中添加元素 x
      • remove(x):从集合中移除元素 x,若元素不存在则会抛出 KeyError 异常。
      • discard(x):从集合中移除元素 x,若元素不存在不会抛出异常。
      • clear():移除集合中的所有元素。
      • 集合支持交集、并集、差集、对称差等运算。
  2. frozenset
    • frozenset 是不可变的集合类型,一旦创建后无法修改。
    • frozensetset 相比,最大的区别是它是不可变的,因此可以作为字典的键或作为其他集合的元素。
    • 创建方式:
      • 使用 frozenset() 函数创建冻结集合。
    • 特性:
      • frozenset 不支持像 add()remove() 等修改操作。
      • 由于不可变性,它是 hashable 的,因此可以作为字典的键或其他集合的元素。

映射类型

映射类型对象表示由任意索引集合索引的对象集合。在 Python 中,映射通过键值对存储数据,其中每个键都关联一个值。通过键可以快速访问对应的值,映射中的元素可以通过下标(a[k])进行访问,并可以作为赋值或删除操作的目标。映射的条目数可以通过 len() 函数获取。

  • dict 是 Python目前有唯一的内建映射类型,表示由键值对组成的集合。每个键都与一个值关联,键是唯一的,并且可以是任意不可变类型(如字符串、数字、元组等)。
  • 创建方式:使用 {}dict() 构造函数创建
  • 字典的特点:
    • 字典是可变的,可以通过键值对的方式添加、修改或删除元素。
    • 通过键 k 可以访问字典中的值 a[k]
    • 字典支持键值对的删除操作,可以使用 del a[k] 删除某个键。
    • 字典的键必须是不可变类型(如字符串、数字、元组等),并且需要是可哈希的。
    • 字典中的键按照插入顺序存储,这意味着它们会保持插入时的顺序。在 Python 3.6 之前,字典没有保证顺序。在 Python 3.7 中,字典会保留插入顺序,这成为了语言的正式特性。
  • 字典常用方法:
    • a.keys():返回字典的所有键。
    • a.values():返回字典的所有值。
    • a.items():返回字典的所有键值对。
    • a.get(k):返回键 k 对应的值,如果键不存在,则返回 None(也可以指定默认值)。
    • a.update(b):用字典 b 的键值对更新字典 a
    • a.pop(k):删除并返回键 k 对应的值。
    • a.clear():清空字典。
  • 键的比较规则:
    • 字典的键遵循正常的数字比较规则。例如,如果 11.0 被用作字典的键,它们是相等的,因此它们会索引同一个字典条目。
    • 字典中的键必须是可哈希的对象,因此像列表、字典等可变对象不能作为字典的键。

可调用类型

可调用类型是可以像函数一样通过括号进行调用的对象。函数、方法、生成器函数等类型都属于可调用类型。它们可以用于函数调用操作,具体包含以下几种类型:

  • 用户定义函数:用户定义的函数对象是通过函数定义创建的,并且在调用时,必须传递与函数形参一致的参数。
    • 特殊只读属性
      • function.__globals__:对存放函数全局变量的字典的引用(函数定义所在模块的全局命名空间)。
      • function.__closure__:指向函数闭包的单元对象,包含函数自由变量的绑定。
    • 特殊可写属性
      • function.__doc__:函数的文档字符串。
      • function.__name__:函数的名称。
      • function.__qualname__:函数的完全限定名。
      • function.__module__:函数所在模块的名称。
      • function.__defaults__:函数默认参数值的元组。
      • function.__code__:函数的代码对象。
      • function.__dict__:支持任意函数属性的命名空间。
      • function.__annotations__:函数的参数和返回值类型注解字典。
      • function.__kwdefaults__:仅限关键字参数的默认值字典。
  • 实例方法:实例方法是类的成员方法,它将类实例作为第一个参数传递给函数。
    • 特殊只读属性
      • method.__self__:指向方法绑定的类实例对象。
      • method.__func__:指向原始的函数对象。
      • method.__doc__:方法的文档字符串。
      • method.__name__:方法的名称。
      • method.__module__:方法定义所在模块的名称。
    • 实例方法的特点是
      • 当通过类的实例访问用户定义的函数对象时,方法会自动绑定实例,__self__ 会指向该实例。
      • 绑定方法后,调用方法时,第一个参数会自动传递类实例。
  • 生成器函数:生成器函数是包含 yield 语句的函数。调用生成器函数时,返回的是一个迭代器对象,可以通过该迭代器的 __next__() 方法获取生成的值。直到生成器函数执行到 return 或结束时,抛出 StopIteration 异常。
  • 协程函数:协程函数是通过 async def 定义的函数,返回一个协程对象。它可能包含 await 表达式,并且支持异步操作。
  • 异步生成器函数:异步生成器函数结合了协程和生成器的特性,使用 async defyield 语句。调用时返回一个异步迭代器对象,可以在 async for 中使用。异步生成器可以通过 __anext__() 方法返回可等待对象,直到生成器结束,抛出 StopAsyncIteration 异常。
  • 内置函数:内置函数是由 Python 提供的、为特定 C 函数封装的函数。例如 len()math.sin() 都是内置函数。它们的参数数量和类型由 C 函数决定。
    • 特殊只读属性
      • __doc__:函数的文档字符串。
      • __name__:函数的名称。
      • __self__:通常为 None,表示函数本身没有实例绑定。
      • __module__:函数定义所在模块的名称。
  • 内置方法:内置方法是内置函数的特殊形式,通常作为对象的方法存在。例如列表的 append() 方法,它是列表对象的一个方法,隐式地将对象作为第一个参数传入。
  • :类也是可调用的对象,通常用于创建类的实例。类的调用会触发 __new__()__init__() 方法。
  • 类实例:如果在类实例中定义了 __call__() 方法,那么该实例可以变成可调用对象,允许像函数一样调用实例。

模块类型

在 Python 中,模块是代码的基本组织单元,通过导入系统创建,通常可以通过 import 语句或其他方式(如 importlib.import_module())来导入。模块对象的命名空间由字典对象管理,即模块中定义的所有函数的 __globals__ 属性指向的字典。

  • 模块对象上与导入相关的属性
    1. module.__name__
      • 用于在导入系统中唯一地标识模块的名称。对于直接执行的模块,这将被设为 __main__。如果模块属于某个包,__name__ 将是模块的完整限定名称(例如 package.module)。
    2. module.__spec__
      • 记录与模块导入相关的状态,包括模块的规范。这是 Python 3.4 版本引入的特性,表示了模块的导入细节(如加载器、路径等)。通过 module.__spec__ 可以访问模块的加载信息,如:
        • __spec__.name: 模块的名称
        • __spec__.loader: 模块的加载器
        • __spec__.parent: 模块所属的父级包
        • __spec__.submodule_search_locations: 子模块的搜索路径等
    3. module.__package__
      • 模块所属的包的名称。如果模块是顶层模块(即不属于任何包),则该属性为 ""(空字符串)。如果是子模块,则该属性为其包的名称(例如对于 package.submodule__package__package)。
      • 如果模块是顶级模块(即不属于任何包),则 __package__ 默认是空字符串。
    4. module.__loader__
      • 模块加载器对象,表示导入该模块的具体实现。该属性通常用于调试和进一步的加载功能。
    5. module.__path__
      • 用于包的模块,表示包内子模块的搜索路径。对于非包模块,此属性不存在。
    6. module.__file__module.__cached__
      • __file__ 表示从文件加载的模块的文件路径。
      • __cached__ 表示模块的缓存文件路径(如字节编译后的文件)。这两个属性通常在文件加载的模块中存在,但某些类型的模块可能没有这些属性(例如 C 扩展模块)。
  • 模块对象上的其他可写属性
    1. module.__doc__
      • 模块的文档字符串,如果没有文档字符串,则为 None
    2. module.__annotations__
      • 包含在模块体执行期间收集的所有变量注释(如类型注解)。它是一个字典,存储了所有在模块内定义的类型注解。
  • 模块字典
    • module.__dict__
      • 模块的命名空间,它是一个字典对象,存储模块中定义的所有变量、函数和类。module.__dict__ 允许我们动态访问和修改模块内的属性。

自定义类

在 Python 中,自定义类通常通过类定义来创建,每个类都有一个通过字典对象实现的命名空间。类属性引用会被转化为在类的 __dict__ 字典中查找。如果未在该字典中找到某个属性,会继续在基类中查找。基类查找遵循 C3 方法解析顺序 (C3 MRO),即使存在 ‘钻石形’ 继承结构,多个继承路径指向同一个共同祖先,依然能够保持正确的行为。

  • 类的属性与行为:

    1. 类的属性查找
      • 当通过 C.x 访问类的属性时,Python 实际上会去 C.__dict__ 字典中查找 x。如果没有找到,搜索会继续进行到基类(如果有的话)。
    2. 类方法与静态方法
      • 通过类访问方法时,类方法(如 @classmethod)会被转化为一个实例方法,实例方法的 __self__ 属性会绑定到类本身。静态方法(如 @staticmethod)则会转换为该静态方法所封装的对象。
    3. 类属性赋值
      • 类属性的赋值会更新类的 __dict__,但不会影响基类的字典。换句话说,基类的属性不会受到子类对同名属性的修改影响。
    4. 类对象可被调用
      • 类本身可以作为对象调用,从而创建该类的实例。类实例的创建是通过调用类对象的 __new____init__ 方法来完成的。
  • 特殊属性:Python 类有几个特殊属性,用于获取类的相关信息:

属性含义
type.__name__类的名称。
type.__qualname__类的 qualified name。
type.__module__类定义所在模块的名称。
type.__dict__提供类的命名空间的只读视图的映射代理。
type.__bases__类的基类组成的元组,例如:X.__bases__ == (A, B, C)
type.__doc__类的文档字符串,如果未定义则为 None
type.__annotations__包含在类体执行期间收集的变量注释的字典。
type.__type_params__包含泛型类的类型参数的元组(从 Python 3.12 开始)。
type.__static_attributes__包含类内所有通过 self.X 在函数体内赋值的属性名元组(从 Python 3.13 开始)。
type.__firstlineno__类定义的第一行的行号,包括装饰器(从 Python 3.13 开始)。
type.__mro__方法解析顺序(MRO),即查找基类时考虑的类的元组。

特殊方法:除了特殊属性外,Python 类还具有两个特殊方法:

  1. type.mro()
    • 该方法可以被元类重写,以便定制其实例的 MRO(方法解析顺序)。它会在类实例化时被调用,并且结果存储在 __mro__ 属性中。
  2. type.__subclasses__()
    • 每个类都有一个保存直接子类的弱引用的列表。该方法返回一个包含所有当前有效子类引用的列表,列表项按子类的定义顺序排列。

类实例

类实例是通过调用类对象来创建的。每个类实例都有一个独立的命名空间,该命名空间通过字典对象实现。属性查找首先会在该字典中进行,若找不到,则会继续在类属性中查找。

  • 属性查找
    • 类实例会先在其自身的 __dict__ 字典中查找属性。
    • 如果找不到该属性,会继续在类中查找。如果类属性是一个自定义函数对象,它会被转化为一个实例方法对象,并将该实例作为 __self__ 属性。
    • 类方法和静态方法也会被转化成相应的方法对象。详细信息可以参考“类”部分。
  • __getattr__ 方法
    • 如果在实例和类中都找不到所需的属性,而类具有 __getattr__() 方法,则会调用该方法来处理属性查找。
  • 属性赋值与删除
    • 属性赋值和删除会更新实例的 __dict__ 字典,而不会影响类的 __dict__ 字典。
    • 如果类定义了 __setattr__()__delattr__() 方法,这些方法会被调用来处理属性赋值和删除,而不会直接操作实例的 __dict__
  • 特殊方法
    • 类实例可以通过实现一些特殊方法(如 __add____getitem__ 等)来伪装成数字、序列或映射等类型。详细信息可参考“特殊方法名称”部分。
  • 特殊属性:类实例具有一些特殊属性,可以用于获取对象的相关信息:
属性含义
object.__class__类实例所属的类。
object.__dict__一个用于存储对象的(可写)属性的字典或其他映射对象。并非所有实例都具有该属性;详情请参阅 __slots__

I/O 对象

文件对象表示一个打开的文件。在 Python 中,有多种方式可以创建文件对象,主要包括以下几种:

  1. open() 内置函数
    • 用于打开文件并返回一个文件对象。
    • 语法:open(filename, mode)filename 为文件名,mode 为文件打开模式(如 'r', 'w', 'b' 等)。
  2. os.popen()
    • 用于打开一个管道并返回一个文件对象,该文件对象可以用于与子进程进行通信。
  3. os.fdopen()
    • 用于通过文件描述符(文件的整数标识符)返回一个文件对象。通常与低级 I/O 操作一起使用。
  4. socket.makefile()
    • 用于将一个套接字(socket)对象包装成文件对象,以便通过标准文件接口进行读写操作。

sys.stdinsys.stdoutsys.stderr 会初始化为对应于解释器标准输入、输出和错误流的文件对象;它们都会以文本模式打开,因此都遵循 io.TextIOBase 抽象类所定义的接口。
在 Python 中,内部类型包括一些为解释器使用的对象,这些类型也可以暴露给用户,供调试、反射等用途。以下是一些关键的内部类型:

特殊方法名称

在 Python 中,特殊方法(也称为魔法方法或 dunder 方法)允许开发者通过定义方法来实现特定的操作,例如算术运算、索引访问、迭代等。通过这些方法,类能够模拟内置类型的行为,或是为自定义的类提供一些特殊操作。这种特性被称为运算符重载。

基本定制

以下特殊方法可以帮助你控制对象的行为,使它们能与 Python 内置的操作符和函数(如 str(), print(), 比较操作符等)兼容。

  • __new__(cls[, ...]):用于创建类的新实例。常见的做法是调用 super().__new__(cls) 来创建实例,并进行修改。特别用于不可变类型的子类,或自定义元类。
  • __init__(self[, ...]):在实例创建后初始化它。此方法会在 __new__() 创建实例后被调用。返回值必须是 None
  • __del__(self):在实例销毁时调用。这个方法并不总是被保证执行,特别是在解释器退出时。
  • __repr__(self):返回对象的“官方”字符串表示,通常用于调试。希望返回一个有效的 Python 表达式,能够重建对象。
  • __str__(self):返回对象的“非正式”字符串表示,通常是用户可读的格式,适用于打印和格式化。
  • __bytes__(self):通过 bytes() 转换对象为字节字符串,返回值应为 bytes 对象。
  • __format__(self, format_spec):通过 format() 或格式化字符串调用,生成对象的“格式化”字符串表示。
  • __lt__(self, other)__le__(self, other)__eq__(self, other)__ne__(self, other)__gt__(self, other)__ge__(self, other):这些是“富比较”方法,用于实现 <<===!=>>= 运算符的行为。
  • __hash__(self):返回对象的哈希值,用于支持集合和字典操作。
  • __bool__(self):实现布尔值测试,通常与 bool() 函数一起使用。

自定义属性访问

可以定义下列方法来自定义对类实例属性访问的具体含义。

  1. __getattr__(self, name):当属性无法通过正常机制找到时被调用,返回计算的属性值或引发 AttributeError
  2. __getattribute__(self, name):每次访问属性时都会调用此方法。若定义了 __getattr__(),则必须显式调用以避免递归。
  3. __setattr__(self, name, value):在给属性赋值时被调用,用于替代默认的赋值机制。需要通过基类调用 object.__setattr__() 来设置属性。
  4. __delattr__(self, name):在删除属性时被调用。只有当删除属性 del obj.name 有意义时才实现。
  5. __dir__(self):当调用 dir() 时被调用,返回可迭代对象,供 dir() 排序使用。

实现描述器

调用描述器

协程

导入模型

Python 把各种定义存入一个文件,在脚本或解释器的交互式实例中使用。这个文件就是模块 ,模块是包含 Python 定义和语句的文件。其文件名是模块名加后缀名 .py 。在模块内部,通过全局变量 __name__ 可以获取模块名,模块中的定义可以 导入到其他模块中。

import fibo

此操作不会直接把 fibo 中定义的函数名称添加到当前命名空间中,它只是将模块名称 fibo 添加到那里, 使用该模块名称你可以访问其中的函数。

模块

模块包含可执行语句及函数定义。这些语句用于初始化模块,且仅在 import 语句 第一次遇到模块名时执行。(文件作为脚本运行时,也会执行这些语句)。每个模块都有自己的私有命名空间,它会被用作模块中定义的所有名称的全局命名空间。

模块可以导入其他模块。 根据惯例可以将所有 import 语句都放在模块的开头但这并非强制要求。 如果被放置于一个模块的最高层级,则被导入的模块名称会被添加到该模块的全局命名空间。还有一种 import 语句的变化形式可以将来自某个模块的名称直接导入到导入方模块的命名空间中。 例如:

from fibo import fib, fib2

还有一种变体可以导入模块内定义的所有名称:

from fibo import *

这种方式会导入所有不以下划线(_)开头的名称。大多数情况下,不要用这个功能,这种方式向解释器导入了一批未知的名称,可能会覆盖已经定义的名称。

模块搜索路径

当导入一个名为 spam 的模块时,解释器首先会搜索具有该名称的内置模块。 这些模块的名称在 sys.builtin_module_names 中列出。 如果未找到,它将在变量 sys.path 所给出的目录列表中搜索名为 spam.py 的文件。 sys.path 是从这些位置初始化的:

  • 被命令行直接运行的脚本所在的目录(或未指定文件时的当前目录)。
  • PYTHONPATH (目录列表,与 shell 变量 PATH 的语法一样)。
  • 依赖于安装的默认值(按照惯例包括一个 site-packages 目录,由 site 模块处理)。

初始化后,Python 程序可以更改 sys.path。脚本所在的目录先于标准库所在的路径被搜索。这意味着,脚本所在的目录如果有和标准库同名的文件,那么加载的是该目录里的,而不是标准库的。这一般是一个错误,除非这样的替换是你有意为之。

标准模块

Python 自带一个标准模块的库, 一些模块是内嵌到解释器里面的, 它们给一些虽并非语言核心但却内嵌的操作提供接口,要么是为了效率,要么是给操作系统基础操作例如系统调入提供接口。 这些模块集是一个配置选项, 并且还依赖于底层的操作系统。

dir() 函数

内置函数 dir() 用于查找模块定义的名称。返回结果是经过排序的字符串列表,没有参数时,dir() 列出当前已定义的名称,不会列出内置函数和变量的名称。这些内容的定义在标准模块 builtins 中。

包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。 就像使用模块可以让不同模块的作者不必担心彼此的全局变量名一样。导入包时,Python 搜索 sys.path 里的目录,查找包的子目录。

需要有 __init__.py 文件才能让 Python 将包含该文件的目录当作包来处理。 这可以防止重名的目录如 string 在无意中屏蔽后继出现在模块搜索路径中的有效模块。 在最简单的情况下,__init__.py 可以只是一个空文件,但它也可以执行包的初始化代码或设置 __all__ 变量。

从包中导入 *

使用 from sound.effects import * 时会发生什么?你可能希望它会查找并导入包的所有子模块,但事实并非如此。因为这将花费很长的时间,并且可能会产生你不想要的副作用,如果这种副作用被你设计为只有在导入某个特定的子模块时才应该发生。

唯一的解决办法是提供包的显式索引。import 语句使用如下惯例:如果包的 __init__.py 代码定义了列表 __all__,运行 from package import * 时,它就是被导入的模块名列表。发布包的新版本时,包的作者应更新此列表。如果包的作者认为没有必要在包中执行导入 * 操作,也可以不提供此列表。

如果没有定义 __all__from sound.effects import * 语句不会把包 sound.effects 中的所有子模块都导入到当前命名空间;它只是确保包 sound.effects 已被导入(可能还会运行 __init__.py 中的任何初始化代码),然后再导入包中定义的任何名称。 这包括由 __init__.py 定义的任何名称(以及显式加载的子模块)。

请注意子模块可能会受到本地定义名称的影响。 例如,如果你在 sound/effects/__init__.py 文件中添加了一个 reverse 函数,from sound.effects import * 将只导入 echosurround 这两个子模块,但 不会 导入 reverse 子模块,因为它被本地定义的 reverse 函数所遮挡。

多目录中的包

包还支持一个特殊的属性, __path__ 。 在执行该文件中的代码之前,它被初始化为字符串的 sequence,其中包含包的 __init__.py 的目录名称。这个变量可以修改;修改后会影响今后对模块和包中包含的子包的搜索。这个功能虽然不常用,但可用于扩展包中的模块集。

http://www.dtcms.com/a/399484.html

相关文章:

  • 湖州公司做网站南山龙岗最新通告
  • 南通建设招聘信息网站石家庄网站建设服务
  • 网站配资公司网站网站推荐免费的
  • asp旅游网站模板下载阜新本地网站建设平台
  • DBA 系统学习计划(从入门到进阶)
  • 列出网站目录wordpress正文底部版权声明
  • 网站改版建设 有哪些内容什么叫关键词
  • 郴州网站建设设计制作西安开发网站建设
  • 深度解析:vLLM PD分离KV cache传递机制全解析
  • 六维力传感器和关节扭矩传感器:机器人精准控制的“内外双核”
  • 什么是TCP/UDP/HTTP?它们如何影响你的内网穿透体验?
  • 如何制作大气网站公司变更流程
  • docker概念、安装与基本使用
  • 文件操作的相关知识
  • 网站建设不足之处网站seo案例
  • 卖网站赚钱吗做国外网站翻译中国小说赚钱
  • python爬虫--requests模块
  • kernel4.19 rk3568 buildroot perf 编译踩坑记录
  • Springboot实现WebSocket通信(一)
  • wordpress站点标题360免费自助建站
  • 网站开发前端设计二维码创意设计
  • 太原网站建设随州steam交易链接怎么改
  • 网站风格设计怎么写织梦搞笑图片网站源码
  • 网站建设 中企动力板材生态板跨境电商平台建设方案
  • STM32 单片机 - ADC
  • STM32_08_中断(☆☆☆)
  • 网站建设合优wordpress首页添加视频
  • todesk开启虚拟屏后被控黑屏
  • 学校网站建设的申请书推广100种方式
  • 瑞芯微RK3506核心板/开发板DSM音频开发实战,关键知识点与实操案例必看攻略!