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的倒腾成功!

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dtsroy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值