IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 2-折腾python:继续一些瞎打印的小把戏 -> 正文阅读

[Python知识库]2-折腾python:继续一些瞎打印的小把戏

让list打印时顺便输出索引

首先,作为基本素质,请先备份源码免得搞坏了又要下载.其次,方便自己,把玩过的东西改回去,免得自己把自己绕着了(比如应该是True的你给我打印一个False我可记不清楚呢)

定位到static PyObject *list_repr(PyListObject *v)函数(记住了,_repr后缀应该就是负责打印的,以后都对这种函数下手就完了)
位于Objects/listobject.c:361,贴出来函数体吧

Py_ssize_t i;
PyObject *s;
_PyUnicodeWriter writer;

if (Py_SIZE(v) == 0) {
    return PyUnicode_FromString("[]");
}

i = Py_ReprEnter((PyObject*)v);
if (i != 0) {
    return i > 0 ? PyUnicode_FromString("[...]") : NULL;
}

_PyUnicodeWriter_Init(&writer);
writer.overallocate = 1;
/* "[" + "1" + ", 2" * (len - 1) + "]" */
writer.min_length = 1 + 1 + (2 + 1) * (Py_SIZE(v) - 1) + 1;

if (_PyUnicodeWriter_WriteChar(&writer, '[') < 0)
    goto error;

/* Do repr() on each element.  Note that this may mutate the list,
   so must refetch the list size on each iteration. */
for (i = 0; i < Py_SIZE(v); ++i) {
    if (i > 0) {
        if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0)
            goto error;
    }

    s = PyObject_Repr(v->ob_item[i]);
    if (s == NULL)
        goto error;

    if (_PyUnicodeWriter_WriteStr(&writer, s) < 0) {
        Py_DECREF(s);
        goto error;
    }
    Py_DECREF(s);
}

writer.overallocate = 0;
if (_PyUnicodeWriter_WriteChar(&writer, ']') < 0)
    goto error;

Py_ReprLeave((PyObject *)v);
return _PyUnicodeWriter_Finish(&writer);

error:
_PyUnicodeWriter_Dealloc(&writer);
Py_ReprLeave((PyObject *)v);
return NULL;

然后勉强能阅读这些代码,就是先输出一个"[",然后根据情况遍历整个列表,分别调用其repr函数(这个有些特殊,内部机制实际上是函数指针),然后循环下一个,直到最后一个就输出一个"]".特别好理解.下面来搞事情.
我们先来试试输出值和它的索引,热热身.
在for外部声明一个局部变量.char *tmp_s = malloc(100);,并分配空间;
然后在每次循环的时候格式化一下索引并修改输出函数的参数,让他打印tmp_s;
最后一个对象,特殊处理就完啦~
代码如下:Objects/listobject.c:385-413

char *tmp_s = (char*)malloc(100);
/* Do repr() on each element.  Note that this may mutate the list,
   so must refetch the list size on each iteration. */
for (i = 0; i < Py_SIZE(v); ++i) {
    sprintf(tmp_s, " : %d, ", i);
    if (i > 0) {
        //if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0)
        if (_PyUnicodeWriter_WriteASCIIString(&writer, tmp_s, strlen(tmp_s)))
            goto error;
    }

    s = PyObject_Repr(v->ob_item[i]);
    if (s == NULL)
        goto error;

    if (_PyUnicodeWriter_WriteStr(&writer, s) < 0) {
        Py_DECREF(s);
        goto error;
    }
    Py_DECREF(s);
}

writer.overallocate = 0;
// i的值未改变,还是最后一个索引值
//if (_PyUnicodeWriter_WriteChar(&writer, ']') < 0)
// 需要调用输出字符串的函数!
sprintf(tmp_s, " : %d]", i);
if (_PyUnicodeWriter_WriteASCIIString(&writer, tmp_s, strlen(tmp_s)) < 0)
    goto error;

运行效果:
实现一半.
实现了预期效果,但…这个索引从1开始很离谱,我们修改一下:
把L389和L411的sprintf第三个参数改成i-1.
正常啦
好,既然如此,那我们给他搞个大事情:全给我倒着输出!
(偷偷删掉刚刚的恶作剧)
实现原理:修改v->ob_item的索引,改成"数组大小-前索引-1",就是反着遍历啦
修改后的代码
运行,正常:
列表反过来打印了!
突发奇想,如果嵌套列表会怎么样?肯定都反过来,下图为证:
nice

改变dict键值位置

折腾玩list,怎么能放过dict?!
定位static PyObject *dict_repr(PyDictObject *mp)@Objects/dictobject.c:2093(足够后面…幸好有搜索功能)

简单起见哈,我们仅仅改变key和value的值(这足够过分了,当你的朋友看见不可哈希对象(比如list)出现在键的位置,咱可以想象一下那表情[])

首先找到L2123循环开始,其他的可以简单浏览下,都能看懂的相信.仅仅讲一下很重要的两个宏(每个源码解析书都会说到,我自然不能落后对不对)Py_INCREFPy_DECREF

跟着vs的访问,找到这个宏一步步展开的最终结果:(以INCREF为例)
object.h:475->object.h:461(函数),手动找到实现,结果无能为力啊…那就直接说吧
分词:INC(increase) REF(reference) 增加引用(计数)
众所周知每个PyObject结构体都有个引用计数,这根gc(垃圾回收)紧密相连,python虚拟机的垃圾回收机制可以肤浅的理解成,维护每个对象的引用计数器,每次引用变量就自增1,当那个引用的变量被del(析构,或删除)后,自减1,gc要做的事就是在引用计数变成0的时候发现,并干掉这个没用的垃圾

当然,这么复杂的解释器内部肯定不只说的这么简单,它需要考虑是事情更多,优化也很重要,勉强这么理解就行了.

下面正式向dict进攻:
代码片段
熟练地恶作剧技巧告诉我们,这个repr字眼肯定是打印用的,事实也正是如此,我深入看过它的实现,特别复杂,函数调用有接近十层(真没吹)

这里也顺便引出阅读大型项目源码的一大难点:他为了全局上的方便,牺牲局部的可读性.什么意思呢?就是说,你一个不是它项目组的人去看,多半看不大懂,因为你脑子里没有开发者脑子里的结构图,你不知道哪个函数是哪个逻辑层(我喜欢这么称呼),也不清楚它会调用到哪里去,或者被谁调用.源码阅读者若不是修养特别深,多半脑海里都仅仅是一个个散着的函数(本人自己写过中型项目,我可以清晰说出我项目里的层次而你看半天估计也看不明白),因此这里我就把关键的调用节点都给大家调查好,毕竟vs操作不那么方便(或者我不熟练,我喜欢vscode)

回归正题,为了实现目标(改变键值位置),关注到英文单词valuekey出现的位置,如截图,在L2137和L2148位置,试着调换他们应该就行.如下:
改代码
(还是那句话,记得改回来哦)
运行情况
单凭那个[123, 234]:'abc'就足够吓懂python的朋友一跳了哈哈哈
(还有那个集合作为键,真的特别反人类),ok,dict的倒腾成功!

关于如何搭建环境,请看上一章
玩腻了输出的把戏,下一章我们来盘一下运行时~

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:02:34  更:2022-01-29 23:02:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:17:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码