Python标准库运用eval拼接与描述符机制构建namedtuple

【python】你听说过namedtuple嘛?会用嘛?知道它实现的原理嘛? — 码农高天

码农高天

尽管编程规范普遍规避动态编译,Python核心库通过前置严格标识符校验并借由eval构建__new__方法,配合_tuplegetter描述符在原生元组上以极简开销实现了兼具属性补全与不可变性的轻量数据结构。

AI-assisted summary of 【python】你听说过namedtuple嘛?会用嘛?知道它实现的原理嘛? — original source. Check the original for full context.

Source published:

Key ideas

  • namedtuple 作为动态类工厂函数,在保留原生 tuple 极低开销与不可变性的同时,提供了属性名点号访问和 IDE 智能补全,解决了手写 class 冗余与 dict 易拼写错误的痛点。 (0:00)
  • namedtuple 的底层通过继承 tuple 实现,其属性点号访问是基于 operator.itemgetter 与 property 描述符动态绑定到命名空间完成映射的。 (5:00)
  • Python 标准库在构造 namedtuple 的 __new__ 初始化逻辑时,采用了常被教程规避的字符串代码拼接与动态编译执行方案,并依靠前置的严格合法性校验保障安全性。 (10:00)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2024 年 2 月 16 日发布。视频简介指出,本期主要探讨相对小众的数据结构 namedtuple,通过介绍其实用特性,帮助开发者在处理杂乱数据时有效减少写出 bug 的概率。

## TL;DR 视频首先对比了手写 class、普通 tuple 和字典在处理轻量坐标数据时的痛点,引出兼具不可变性、IDE 补全友好及点号访问优势的 namedtuple。随后深入标准库源码,揭示了 namedtuple 作为 tuple 子类,如何通过 operator.itemgetter 与 property 巧妙实现属性到元组索引的映射。最后剖析了官方标准库使用字符串拼接与 eval 动态生成 __new__ 方法的“叛逆”实现,并辩证地解释了这种技术选择的合理性与安全性。

## Atmosphere & style 整体氛围轻松诙谐且富有极客探究精神,开场关于 tuple 发音的调侃迅速拉近了与观众的距离。讲解过程层层递进,既有面对工程实际痛点的务实权衡,也有深挖 Python 标准库黑魔法时的豁然开朗。面对常被视作禁忌的 eval 拼接代码,UP 主展现出辩证求真的技术态度,带给观众兼具实战启发与底层源码震撼的硬核观看体验。

## Bulletin - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) namedtuple 作为动态类工厂函数,在保留原生 tuple 极低开销与不可变性的同时,提供了属性名点号访问和 IDE 智能补全,解决了手写 class 冗余与 dict 易拼写错误的痛点。 - [05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) namedtuple 的底层通过继承 tuple 实现,其属性点号访问是基于 operator.itemgetter 与 property 描述符动态绑定到命名空间完成映射的。 - [10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) Python 标准库在构造 namedtuple 的 __new__ 初始化逻辑时,采用了常被教程规避的字符串代码拼接与动态编译执行方案,并依靠前置的严格合法性校验保障安全性。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) 针对诸如 2D 坐标等轻量数据结构,手写完整 class 会引入过多的冗余模板代码,在一次性使用场景下显得小题大做。 - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) 使用原生 tuple 会导致代码中充斥 p[0]、p[1] 等数字索引,严重牺牲代码的可读性与语义表达。 - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) 使用字典 dict 存储轻量数据虽然保留了语义,但缺乏 IDE 属性补全提示,且易因拼写错误随意塞入非法 key 而引入 bug。 - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) namedtuple 结合了各方案优势,只需传入类名与字段列表即可一行完成定义,既支持索引也支持语义化的属性点号访问。 - [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) 作为不可变数据结构,namedtuple 天然具备防篡改特性,非常适合处理网络解包等只读数据的场景。 - [05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) namedtuple 完全基于纯 Python 实现,生成的动态类直接继承自原生 tuple 基类。 - [05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) 支持完整的解包解构赋值语法,并自带 _asdict() 方法方便在字典与命名元组之间相互转换。 - [05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) 属性点号访问机制优先调用 C 扩展 _collections 中的 _tuplegetter,并在缺失时回退到纯 Python lambda 实现。 - [05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) 纯 Python 回退机制使用 operator.itemgetter 提取元组对应索引,再借由 property 伪装为描述符属性读取。 - [10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) 由于元组不可变的本质,实例的构建必须在 __new__ 阶段完成,而非普通的 __init__ 构造器。 - [10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) 标准库通过字符串代码格式化拼接与动态编译来构建 __new__ 方法,打破了常规教程对 eval/exec 的绝对避忌。 - [10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) 官方源码之所以敢用动态字符串拼接,是因为在前序步骤中对所有字段标识符和关键字执行了极其严苛的合法性过滤。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=0s) 轻量级数据结构的痛点与 `namedtuple` 的引入

在日常编写 Python 程序时,开发者常常需要处理一些小巧的数据结构。码农高天在开场调侃道,如果对 “tuple” 这个词的发音有意见可以参考他专门讲发音的视频,这里不再多费口舌。他以 2D 游戏或游戏 AI 中表示坐标点(x, y)为例,梳理了三种传统的数据保存方案及其局限性。第一种是专门为该结构手写一个 `class`,虽然结构最完善且适用于高频复用场景,但若数据只是网络通信中一次性传过来的玩家坐标,“线单独为他写一个class呢有时候稍显荣誉(冗余)”,显得小题大做。第二种是直接用普通的 `tuple` 打包传走,虽然快捷,却会导致代码充斥着类似 `p[0]`、`p[1]` 这种纯下标访问,久而久之不仅让人想不起 0 和 1 各自代表什么含义,还会严重破坏代码的可读性。第三种是用 `dict`(字典)将 x 和 y 作为 key 保存,这种 JavaScript 风格的做法看似折中并保留了语义,但使用者依然要死记 key 的拼写;一旦拼错或者写错,Python 字典的灵活性会导致代码接受了诸如 `xx` 或 `z` 这种非法 key,IDE 也无法给予任何自动补全提示,极易引入潜在 bug。此外,无论是自定义对象还是字典,数据默认都有可能被随意篡改,而在某些场景下不可变性反而是防止 bug 的关键。

针对上述困境,高天引出了本期的主角——`namedtuple`。他明确表示:“你不能说他比其他的方案都优秀但是它确实解决了一些我们上面提到的问题。” 本质上,`namedtuple` 是一个动态构建类的工厂函数。创建它只需要两个核心参数:第一个是新建类的类名,第二个是包含各个字段名称的列表或元组(即 field names),相当于给元组的每个位置赋予了显式的名字。实例化后,它既保留了原生 tuple 的特性,支持方括号下标索引访问(如 `p[0]`),同时也允许通过属性访问语法通过点号取值(如 `p.x`)。与普通元组相比,它拥有明确的语义;与字典相比,IDE 能够精准提供属性名自动补全并在拼写错误时立即报错;与手写完整的类相比,它只需一行代码即可定义完毕,大幅精简了冗余模板代码。

| 方案 | 优点 | 缺点 | |:--- |:--- |:--- | | **手写 class** | 结构完善、功能最丰富、可读性好 | 定义繁琐、模板代码冗余,一次性使用成本高 | | **普通 tuple** | 极其轻量、内存小、速度快、不可变 | 代码充满 `p[0]`、`p[1]` 等数字索引,缺乏可读性 | | **字典 dict** | 支持键值对访问,可读性好于纯 tuple | 允许随意添加非法 key,IDE 无法智能提示,存在拼写致错风险 | | **namedtuple** | 简洁声明、支持点号访问与索引访问、不可变、IDE 补全友好 | 动态构建,灵活性不及普通 class |

```mermaid flowchart TD accTitle: Lightweight Data Structure Approaches accDescr: Comparison of different lightweight data representation methods in Python leading to namedtuple. subgraph Traditional["传统轻量数据方案"] A["手写 class\n代码完善但定义冗长"] B["普通 tuple\n轻量但充斥 p[0], p[1] 索引"] C["字典 dict\n支持键值访问但无 IDE 补全且易拼错"] end subgraph Solution["优化解决方案"] D["namedtuple\n动态生成轻量类"] D -->|"特性 1"| E["支持属性点号访问 p.x 与 IDE 补全"] D -->|"特性 2"| F["兼容元组索引访问 p[0] 及解构赋值"] D -->|"特性 3"| G["保持元组不可变性 (immutable)"] end ```

---

[05:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=300s) 源码剖析:继承 `tuple` 与 `_tuplegetter` 属性黑魔法

高天指出,虽然 Python 后来推出了功能更强大的 `dataclass`,但 `namedtuple` 的底层实现非常耐人寻味。其源码完全由纯 Python 编写,没有复杂的底层 C 扩展羁绊,却蕴含了许多巧妙的“黑魔法”。在标准库注释与文档中,`namedtuple` 还支持诸多实用特性:它可以像普通元组一样直接进行解包解构(如 `x, y = p`),自带 `_asdict()` 方法将其直接转成字典,也能直接通过字典解包实例化对象。随后高天切入源码的类定义逻辑:`namedtuple` 动态创建类时,其元类调用将 `tuple` 作为基类传入(`base class` 为 `tuple`),这意味着生成的类本质上就是 `tuple` 的子类,天然继承了原生元组的全部能力与不可变特性。

那么核心机制在于:它是如何做到访问属性 `p.x` 时,自动映射拿到索引为 0 的元素的?高天带着大家逐行解析其类命名空间(class namespace)中的逻辑。系统会遍历传入的每一个字段名称(如 `x` 和 `y`),并在命名空间中将该属性名绑定到一个由 `_tuplegetter` 生成的属性上。对于 `_tuplegetter`,源码先尝试从 C 语言扩展 `_collections` 中导入,若导入失败则回退(fallback)到一段仅有一行的纯 Python 实现:“这个 tuplegetter 是什么呢大家看这里,首先他从这个下滑线 collections 里面尝试 import 一下,那看了之前一期视频的人呢大概就能猜到这个呢就是他 C 的实现如果有 C 的实现的话我用 C 的实现,但它的功能我们可以从它的 fallback 也就是在这个 ImportError 产生的情况下它的 Python 实现来猜一下”。这行纯 Python 代码非常精巧,它是一个接收 `index` 和 `doc` 参数的 lambda 函数,其核心调用了内置的 `property` 和 `operator.itemgetter`:

- **`operator.itemgetter(index)`**:返回一个可调用对象,等价于一个接收 `self` 并执行 `return self[index]` 的函数,作用是从元组中提取第 `index` 个元素。 - **`property(...)`**:平时常作为装饰器使用,本质上是一个描述符构造器。当把它直接包裹在提取函数外部时,它将原本需要带括号调用的函数伪装成了一个普通的属性读取行为。 - **绑定到命名空间**:通过 `class_namespace[name] = _tuplegetter(index, doc)`,属性 `x` 就被绑定成了一个由 `property` 包装的描述符。当用户执行 `p.x` 读取该属性时,Python 底层便会自动调用该函数,执行 `itemgetter` 从元组的第 0 位抓取数据。

---

[10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) 争议实现:字符串拼接与 `eval` 构造 `__new__`

在深入理解了属性绑定的本质后,高天进一步解析了实例创建阶段的细节。由于元组是不可变数据结构(immutable),其属性和数据初始化工作不能在常规的 `__init__` 中执行,而必须在 `__new__` 阶段完成。令人意想不到的是,`namedtuple` 官方源码构建 `__new__` 方法的方式显得极度“叛逆”——它竟然是通过 Python 字符串格式化拼接出函数定义的代码文本,然后直接调用 `eval`(或 `exec` 配合环境)来动态编译生成的。很多 Python 入门书籍和教程都会反复告诫开发者“绝对不要随便使用 eval,不要用字符串拼接代码”,然而 Python 核心标准库本身却堂而皇之使用了这种写法。

面对这种实现,高天给出了辩证的看待方式:“你看很多 Python 书都会告诉你绝对不要这么写代码,这就说明万事万物都要去辩证的看有些技术确实有它的缺陷但也不是不能使用。” 官方通过字符串拼接动态构造了一个参数列表与外部字段完全一一对应的 `__new__` 函数,将外部传参打包后直接交给底层 `tuple.__new__`。之所以敢这样用,是因为源码在前序逻辑中做足了严格的标识符合法性检查与关键字过滤,彻底封堵了代码注入的风险。在视频结尾,高天总结道,`namedtuple` 虽然在各大社区中探讨较少、相对小众,但它在处理临时结构体、网络通讯数据包解包以及轻量级只读数据处理时极为实用且能有效规避手误 bug,非常推荐开发者在合适场景下大胆尝试。

## Notable quotes > [10:00](https://www.youtube.com/watch?v=gSx0KYjZcHs&t=600s) 万事万物都要去辩证的看有些技术确实有它的缺陷但也不是不能使用。 — 码农高天

## Mentioned Python, namedtuple, tuple, dict, dataclass, operator.itemgetter, property, _tuplegetter, eval, exec, 码农高天

## Video description 这期视频我们讲一个比较小众的数据结构namedtuple。这个内容B站上做过的人很少,但是它有一些挺实用的feature。在我们处理一些比较杂的数据的时候,可以有效地降低我们写bug的几率。了解完试用一下才知道好不好用~

[Source](https://www.youtube.com/watch?v=gSx0KYjZcHs)

中文简报

Python标准库运用eval拼接与描述符机制构建namedtuple 尽管编程规范普遍规避动态编译,Python核心库通过前置严格标识符校验并借由eval构建__new__方法,配合_tuplegetter描述符在原生元组上以极简开销实现了兼具属性补全与不可变性的轻量数据结构。 视频首先对比了手写 class、普通 tuple 和字典在处理轻量坐标数据时的痛点,引出兼具不可变性、IDE 补全友好及点号访问优势的 namedtuple。随后深入标准库源码,揭示了 namedtuple 作为 tuple 子类,如何通过 operator.itemgetter 与 property 巧妙实现属性到元组索引的映射。最后剖析了官方标准库使用字符串拼接与 eval 动态生成 __new__ 方法的“叛逆”实现,并辩证地解释了这种技术选择的合理性与安全性。 namedtuple 作为动态类工厂函数,在保留原生 tuple 极低开销与不可变性的同时,提供了属性名点号访问和 IDE 智能补全,解决了手写 class 冗余与 dict 易拼写错误的痛点。 (0:00) namedtuple 的底层通过继承 tuple 实现,其属性点号访问是基于 operator.itemgetter 与 property 描述符动态绑定到命名空间完成映射的。 (5:00) Python 标准库在构造 namedtuple 的 __new__ 初始化逻辑时,采用了常被教程规避的字符串代码拼接与动态编译执行方案,并依靠前置的严格合法性校验保障安全性。 (10:00)

Reading summary

码农高天指出,Python 标准库中的 `namedtuple` 是一种高效的轻量级数据结构工厂,它在兼顾普通元组不可变性与内存优势的同时,通过巧妙的描述符设计与动态编译解决了传统数据表达方案在可读性与类型安全上的痛点。 轻量数据结构的权衡与引入 (0:00) 在处理如二维坐标点(x, y)这类轻量数据时,常见的三种方案各有缺陷。手写完整类虽然结构完善但存在模板代码冗余,用于一次性通信数据显得小题大做;普通元组虽轻便且不可变,但访问依赖数字索引(如 p[0]、p[1]),严重损害代码语义与可读性;字典虽支持键值访问,却容易因拼写失误引入非法键,且 IDE 无法提供自动补全提示。 针对这些痛点,`namedtuple` 作为动态类构建工厂,仅需传入类名与字段列表即可生成新类。其实例既保留了元组的索引访问、不可变性与解包能力,又支持点号属性访问(如 p.x)与 IDE 智能补全,用极低的定义成本实现了语义清晰与防止意外篡改的平衡。 属性映射机制与描述符黑魔法 (5:00) `namedtuple` 生成的类本质上直接继承自原生 `tuple` 基类。为了将属性访问 `p.x` 映射到底层元组的第 0 个元素,其类命名空间在初始化时为每个字段名绑定了 `_tuplegetter` 构造的属性。该机制优先导入 C 语言扩展 `_collections` 实现,在不可用时则回退至纯 Python 实现。 其回退实现是一个结合了 `operator.itemgetter` 与 `property` 的精巧函数。`itemgetter(index)` 负责从实例中按索引提取元素,而内置的 `property` 描述符将其封装为无括号调用的属性读取行为。当代码执行属性访问时,Python 自动触发描述符执行对应索引的切片提取,从而实现零负担的直观语义访问。 动态构造实例与争议实现考量 (10:00) 由于元组具有不可变特性,`namedtuple` 实例的数据初始化不能依赖普通的 `__init__`,必须在 `__new__` 阶段完成。标准库源码在此处采取了通常被编程教程视作反模式的做法:通过字符串格式化拼接出完整的 Python 函数代码,再利用 `eval` 或 `exec` 环境进行动态编译。 高天解释称,虽然业界普遍告诫避免使用字符串拼接与 `eval`,但标准库通过在前序逻辑中对字段名执行严格的合法标识符检查与关键字过滤,彻底排除了代码注入隐患。这种动态编译方式确保了生成的 `__new__` 拥有与外部字段精确对应的签名,体现了在保证输入绝对安全的前提下对实用性与性能的权衡。 尽管后来推出的 `dataclass` 功能更为强大,且 `namedtuple` 在社区探讨中相对小众,但高天认为其在临时数据结构、网络通信数据包解构及只读轻量数据场景下仍具有显著优势,能够以极小开销有效预防低级手误。

Source quotes

[10:00] 码农高天: 万事万物都要去辩证的看有些技术确实有它的缺陷但也不是不能使用。

Original transcript

[0:00] 这期视频我们来讲一下NameTupel对TUPEO这个词发言有想法的朋友可以直接看下方的视频这个事我不想一次一次的说了Tupel这个数据结构本身是python里面非常常用的数据结构我相信看我视频的人肯定都用过也大概知道怎么用因为它很简单但是NameTupel又是什么呢我们知道在我们写程序的过程中经常会处理一些比较小巧的数据那在处理这种数据的时候呢我们有很多种方式我们举一个非常常见的例子如果大家平时写一些2D的游戏或者这些游戏的AI的话 可能经常会遇到这么一个数据结构就是保存某一个点的坐标那这种坐标的形式呢每个人选择的保存方式是不一样的他可能像现在我写的一样单独为这个坐标写一个class他也可能直接用一个tuple来解决问题他把这个x跟y按顺序放到这个tupor里他还可能会动用一个 dictionary把X跟Y当做key,然后把这个座标当做value放进去那这些方式从使用上说肯定各有优劣比如新建一个class这件事它看起来是最完善的那对于重复使用率非常高的术语结构这当然是一个非常好的方法但是很多时候我们拿到这个数据 我们就用一下他可能是从网络一端传回来一个玩家和一个他现在的位置或者一个玩家和一个他即将去往的位置那这个术语结构可能我们只有在通讯跟传输的过程中用一下线单独为他写一个class呢有时候稍显荣誉所以我们时候干脆就把数据放到一个tupole直接一传简单快捷但是放到Tupel里也会有一个问题你会发现你的程序里就开始充斥着一些index就什么东西方块号0什么东西方块号1久而久之这些零跟一你都会不记得它到底是什么在使用的偏多的时候会破坏你程序的一读性那Dictionary呢看起来是这两种方案的一个折中方案他把这个数据的意义以key的形式保存了那经常写JavaScript肯定对这个方法觉得非常亲切但是他也有他的问题就你依然还是需要记住这些key如果是x跟y的话可能挺好记的有时候呢它是一些别的东西这个key 可能你写着写着就忘了怎么拼的了或者是不小心拼错了这时候就会引入一些bug因为字典在python里是一个非常灵活的注意结构你往这个字典里不小心加了一个xx或者不小心加了一个z他也接受而你的IDE也没有办法提示这个字典里面的key应该是什么同时无论是字典还是object它都会面临一个数据有可能会被修改的问题那数据可能被修改这件事呢在某些场合下是必要的feature在某些场合下呢就是无谓的潜在bug的来源那今天我们要介绍的name tuple呢 就是另外一种在这个场景下的解决方案你不能说他比其他的方案都优秀但是它确实解决了一些我们上面提到的问题我们看name推普的方法首先,NameTupel的本质是你新建了一个类它和上面这个class position definition是一样的只不过它这个类是动态建立的随时建立呢你可以随时扔name tuple比较必要的是两个argument第一个是建立这个类的名字这块你当然可以随便写那第二个呢就是一个list或者一个tuple在这里呢他们叫field它相当于是给这个tuple的每一个位置起了一个名字Name tuple嘛就是有名字的tuple我们看在建立了这个类之后,我们使用它和使用其他的类是一样的P4=PER,我们就建立了一个named tuple那从下面两行程序我们可以发现NameTupel一个非常好的地方就是它既可以通过index来取值也可以通过点的形式来取值P40和P41等价于P4.x和P4.y那相比于前面介绍的纯TUple他给每个位置标记上了名字这就让我们摆脱了那些方块号0方块号1同时 由于这个x跟y是它的attribute而不是dictionary的key你的IDE是可以正确的提示他们的并且在你有比误的时候他会报错这就减小了你写bug的几率第三,相比于class definition来说它的代码显然是更简洁的我们把这第十二行 [5:00] B=name tuple 和前面的3456行做一下对比他简洁的还蛮明确的当然,python在这之后呢,又出了data class这个功能也比较相近的比较新的feature我们未来有机会再去讲它那基本的内容介绍完了后面当然就是我们喜闻乐见的实现部分我们看NameTupel的实现相比于之前我们介绍的很多内容,它会更好读。因为它是一个纯拍档的视线只不过在这个过程中他确实也用到了一些黑魔法我们首先看在注释里,他还提到了一些之前我们没有说到的用法比如说你可以像一个正常的TUPLE一样去把它做unpackx都和y等于p你可以用 as dict 来返回一个 dictionary你也可以从一个Dictionary变成这个NameTuple它本身呢还是有很多很方便的feature那就像我前面提到的 name tuple的本质呢 是新建了一个class的类型这个代码呢大家应该并不陌生因为我们之前讲到过如果不记得或者没有看的同学可以翻一下我之前的视频那在这里呢,这个type设置了tuple本身作为base class它就相当于我新建的class继承了tuple所以所有tupel有的功能他都有它是一个加强版的tupo那这些加强的魔法当然就发生在这个class namespace里了我们看他class name space的definition就在上面如果你还记得之前视频我对class的讲解你就知道这个部分基本上相当于在class的definition里面define的函数这些内容大部分没有那么难理解我们可能更关注的是两个事第一在建立的时候他是怎么建立这个tuple的第二,也就是它的核心功能他是怎么做到这个object.x之后就拿到了index为0的那个数据呢我们先来回答后面这个问题 因为后面这个问题就写在下面了对于我们给的每一个field name就是我们前面给的那个x和y他把class name space里面 name对应的value设置成这个duple getter那这个tuple getter是什么呢大家看这里 首先他从这个下滑线collections里面尝试import一下那看了之前一期视频的人呢大概就能猜到这个呢就是他c的视线如果有C的实现的话 我用C的实现但它的功能我们可以从它的fallback也就是在这个import error产生的情况下它的python实现来猜一下我们看它的拍档实现实际上只有一行但是这一行还真不是那么好读推波概念本身是一个lamb的函数这个函数有两个argument 分别是index和doc这个函数返回的是一个property如果你看这个property觉得眼熟的话 你或许可以意识到 我们绝大部分时间是把它当做一个装饰器在使用的这个property呢我们的视频里面没讲过但是肯定很多人都使用过在一个class里面的method上面装饰一个property它代表当我尝试读取这个函数的同名属性的时候调用这个函数也就可能原来我本来是define f 括号self我用的时候呢是我这个object的drf括号我在用property装饰它之后呢就变成了object.f没有这个括号了他把一个函数伪装成了一个属性 但是你读取这个属性的时候 实际上还是调用的这个函数当然,好好的decorator,他怎么能直接使用呢?如果你有这个疑问 请回去翻一翻我前面讲decorator的视频接下来这个item getter这个itemgetter就是operator里面的itemgetter它返回的是一个函数从某种程度上理解呢它更像一个方法那 item getter 括号 index他返回的这个方法本质上就是F call herself,Return self-fangquard index它就是从Tupel里面拿到Dindex的元素好 那我们回头再看这一段代码 它就比较清楚了TupelGator本身是一个函数这个函数返回的还是一个函数返回的函数是被property装饰了的函数装饰的函数是从tupol里面 [10:00] 指定的index的这个功能这里很绕 如果你没有办法理解的话自己尝试把中间的每一步都拆开写开我非常懒 在这就不拆了那让我们回到这个TUpleGatter被使用的地方这一句话实际上就是做到了一个属性名和index的绑定比如说我们刚才的例子,在第一次循环里面,index是0,然后name是x这个时候他就做了一个class namespace x等于tubegetter0dock而tube getter返回的是一个函数这个函数大家记不记得是一个 property也就是在我class新建的instance里面当我使用这个 instance第二 x的时候他实际上就会调用这个property装饰的函数那这个函数也非常简单就是拿到index这个位置它包含的元素这就是他如何做到的我们前文提到的p.x和p0指向同一个元素那最后呢我们稍微提一下刚才讲的这个new我们知道Tupel是一个immutable的数据结构所以它的数据初始化是在new里完成的而不是在init里完成的而讲这个new其中一个很重要的原因呢是因为这个new的实现非常特别大家可以看一下啊这个new是用eval来产生的而上面Eval的code竟然是用string拼起来的这种写法可以说是犯了很多python书上面的大忌你看很多python书都会告诉你绝对不要这么写代码这就说明万事万物都要去辩证的看有些技术确实有它的缺陷但也不是不能使用那这个code本身其实挺简单的它相当于define了一个新的new函数对不对然后这个new函数呢take n个argument他把这些argument打包成一个tuple然后直接放到这个tuple的new函数里这样他就成功的从python的层面建立了一个tuple这也是用了一个字符串拼接 非常有趣那当然前面他还做了很多检查防止这个代码注入的一些漏洞啊Nametupel本身是一个不太大众的数据结构使用它的人并不多包括我自己在准备这期视频之前呢也不是经常会使用到它你去在B站搜一搜 讲他的视频也非常少但是通过今天的讲解 大家应该可以感受到 这还是一个在蛮多场合下挺好用的数据结构它非常的小巧方便在一些临时数据结构的组件或者是一些以类似tuple的形式输入的数据上还是有比较不错的表现的所以大家在了解之后呢也可以尝试使用一下它好那今天视频就到这里希望给大家有所帮助喜欢的话记得点一下关注别忘了一家三连哦优优独播剧场——YoYo Television Series Exclusive