小菜虎的窝
火星日记 ~ 第一千二百二十四天 ~ 半年的终结符
Lee.MaRS 发表于 2009-03-31 18:56:03
经历波澜起伏.
经历漫长等待.
终于等到了.
幻想的破灭.
赶在这三月的最后一天.
丝毫不用怀疑这可能是个April Fool的玩笑.
Reject @ Google, SET
Farewell, Google.
火星日记 ~ 第一千一百三十四天 ~ Happy new year!
Lee.MaRS 发表于 2008-12-31 22:58:31
祝大家新年快乐,心想事成!
Python源码剖析读书笔记可能还会暂停一段时间,因为这项工作实在是非常耗费时间的一件事情,而我现在无法集中精力,静下来把笔记写上来。
不过这项工作还会继续下去,我现在也还在继续阅读这本书,在书上乱涂乱画。
希望大家在新的一年,还能把我的Blog留在你们的Google Reader里面。:-)
火星日记 ~ 第一千一百三十四天 ~ 人生第二封拒信
Lee.MaRS 发表于 2008-12-31 22:22:09
有人和我说过,其实今年百度和微软还是比较好进的。
我和这两家今年都是各有两次机会,也就是说,一共有四次机会摆在我的眼前。
可惜我都没能拿到offer。
第一次是内推面百度。
详情之前我也提到过一些。
总体来说,那次面试面得相当失败。
技术的问题答得一塌糊涂。
一些文化上的问题也冲突严重。
自然是直接被拒了。
第二次是面M$ STBC。
也许是没有经过内推的原因,我接到的是HR电面。
我那一口结巴的英语被HR直接教育了。
然后做Open Problem,SDET职位的。
大概是HR觉得我没有什么资质吧。
收到了人生的第一封拒信。
第三次还是面M$,换了另外一个组。
这次是直接面试。
面试的情况应该还行,考的题目都非常trivial。
就是这样trivial的题目,当时还是让我做得挺难受的。
我自己都不知道该如何解释为什么会这么难受。
然后被电面,问到了一些数据库的简单的东西。
我关系数据库基本上就没有用过,MySQL什么的基本算是不懂。
然后就算电面跪了。
最后没有收到offer,只是问我有没有兴趣去实习,时间是三个月。
我想了一下,拒绝了。
第四次是Baidu。
先是在线笔试。
题目应该算不上很难,但是感觉是挺开放式的题目。
时间也挺紧的。
最后还是通过了在线笔试。
后来接到了电面。
虽然我很想针对面试官提的那些问题吐槽,但这些其实不是最重要的。
对于这次电面,我只后悔一件事情。
就是我在电面的时候由于某种鬼使神差的因素,上网查资料了。
然后也不知道为什么,面试官知道了。
最后,从内部人士那里听说,面试官以这个理由拒了我。
如果是别人的错还好说。
自己犯错误就觉得非常可惜了。
于是,我在2008年的最后一天,收到了来自Baidu的拒信。
=================================================我是拒信的分隔线=================================================
您好!
感谢您应聘“电子商务事业部”职位,通过笔试、面试,
再次感谢您对百度一直以来的关注与信任,以及对我们工作的支持,
谢谢!
百度电子商务事业部
=================================================我是拒信的分隔线=================================================
可以说Baidu毫无人性,在2008年的最后一天发拒信,不让人家过个欢乐的新年。
也可以说Baidu做了件好事,至少不让我把霉运带入2009年。
我和Baidu,和M$,注定是没有缘分的。
火星日记 ~ 第一千一百二十八天 ~ Merry Christmas
Lee.MaRS 发表于 2008-12-25 02:20:29
圣诞夜没闲着, 参加合唱团的走穴活动去了.
四首歌, 唱了两次, 然后就搞定了...
收入两张大红纸...
唱得如何?
恩... 感觉唱得相当的囧...
不过现场的客人看上去根本不在乎你唱什么...
于是我也无所谓了...
反正明年就没我的份了 -.=b
上台唱的时候大家都很矜持.
反倒是在休息的时候唱得很high.
一堆人在那里拼命唱我不会的曲子, 郁...
害得我只好一个人在那里默默地看书...
圣诞夜看别人来吃天价自助餐, 其实心情有一点奇怪的.
zhouye小朋友说以后有钱了也要来这里吃, 再带个小MM...
我就囧住了...
我以后大概没什么机会来吃这种天价自助餐吧...
一是穷...
二是以我的勤俭持家的性格来看应该也不会做这样的事情...
最后离开酒店了以后, 一堆人在路口等着过马路时吃错药了开始唱 Ezekiel Saw The Wheel...
引来无数人侧目...
于是我们开始大声自称是交大的...
回来的时候坐在出租车上, 想想以后要是工作了, 一天的收入还没有一个晚上这样随便唱唱的收入多...
继续囧...
Python源码剖析学习 - Chap3 - Python中的字符串对象 - Part2
Lee.MaRS 发表于 2008-12-14 20:06:00
望对Python有爱的筒子都能购买一本, 支持这本国内难得一见的好书.
转载本文, 请注明作者leemars. 欢迎大家访问作者的博客 http://leemars.ycool.com
[创建PyStringObject对象]
Python提供了两条路径, 从C的原生字符串创建PyStringObject对象. 这两条路径分别是
PyString_FromString和PyString_FromStringAndSize.
先来看PyString_FromString:
[Objects/stringobject.c]
PyObject *
PyString_FromString(const char *str)
{
register size_t size;
register PyStringObject *op;
assert(str != NULL);
size = strlen(str);
if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { /* 1 */
PyErr_SetString(PyExc_OverflowError,
"string is too long for a Python string"); /* 2 */
return NULL;
}
if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
null_strings++;
#endif
Py_INCREF(op); /* 3 */
return (PyObject *)op;
}
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
#ifdef COUNT_ALLOCS
one_strings++;
#endif
Py_INCREF(op); /* 4 */
return (PyObject *)op;
}
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
if (op == NULL)
return PyErr_NoMemory(); /* 5 */
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
Py_MEMCPY(op->ob_sval, str, size+1); /* 6 */
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
nullstring = op;
Py_INCREF(op);
} else if (size == 1) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
return (PyObject *) op;
}
代码还是比较长的. 下面简单说明一下, 顺便提一些Python2.6的变化, 标号对应代码中
注释处:
1. 判断字符串的长度加上PyStringObject本身的大小(即整个变长对象PyStringObject
的实际大小)后, 是否超过系统限制的最大大小. 书上的Python旧版代码中仅判断
size > PY_SSIZE_T_MAX, 也就是没有考虑对象自身的大小, 是不完善的.
2. 当需要分配的内存超过系统限制的上限时, 抛出异常PyExc_OverflowError, 即溢出.
书上的Python旧版代码没有抛出异常.
3. 对于长度为0的空串(即"", 而非NULL), 若nullstring已经被初始化, 则返回
nullstring. 这是intern机制的一部分. 书上的Python旧版代码没有对nullstring增加
引用计数.
4. 对于长度为1的字符串, 若字符表中该字符对象已经初始化, 则返回该字符对象. 这是
intern机制的一部分. 书上的Python旧版代码没有对该字符对象增加引用计数.
5. 若申请内存空间失败, 则调用PyErr_NoMemory()处理. 书上的Python旧版代码中没
这一部分.
6. 使用Py_MEMCPY代替直接使用memcpy. 贴一下Py_MEMCPY的代码:
[Include/pyport.h]
/* Py_MEMCPY can be used instead of memcpy in cases where the copied blocks
* are often very short. While most platforms have highly optimized code for
* large transfers, the setup costs for memcpy are often quite high. MEMCPY
* solves this by doing short copies "in line".
*/
#if defined(_MSC_VER)
#define Py_MEMCPY(target, source, length) do { \
size_t i_, n_ = (length); \
char *t_ = (void*) (target); \
const char *s_ = (void*) (source); \
if (n_ >= 16) \
memcpy(t_, s_, n_); \
else \
for (i_ = 0; i_ < n_; i_++) \
t_[i_] = s_[i_]; \
} while (0)
#else
#define Py_MEMCPY memcpy
#endif
看注释应该就明白了, Py_MEMCPY是为跨平台优化而提供的一个宏. 因为在有些平台上,
调用mempcy的代价比较高, 所以对于小数据量的拷贝就原地展开成循环. 那Python觉得
哪些平台需要这样的优化呢? 看到_MSC_VER了吧, 这个是M$的C编译器特有的宏, 看来,
需要特别优化的就是Windows下了.
书上贴出的Python代码可能出于简化的考虑, 与实际的Python2.5的代码有所出入, 故再
次强调上述几点描述的差异乃是对比Python2.6与书上的代码.
我们不妨也来一次简化, 把所有的异常处理以及intern机制相关内容去掉, 看看这段代
码的核心是什么:
PyObject *
PyString_FromString(const char *str)
{
register size_t size;
register PyStringObject *op;
size = strlen(str);
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
Py_MEMCPY(op->ob_sval, str, size+1);
return (PyObject *) op;
}
是不是变得非常清爽呢? 依次说来就是:
1. 求str的长度
2. 为变长对象PyStringObject申请内存, 地址赋给op.
3. 用宏PyObject_INIT_VAR, 初始化op的头部(包括ob_refcnt, ob_type, ob_size)
4. 将op->ob_shash初始化为-1(未计算)
5. 将op->ob_sstate初始化为SSTATE_NOT_INTERNED(未interned)
6. 将str复制到ob_sval
7. 返回op
之前我们还提到过PyString_FromStringFromSize, 它与PyString_FromString有什么不
同呢? 我们把代码单独摘出来, 用diff比较一下代码:
% diff -uN PyString_FromString.c PyString_FromStringAndSize.c
--- PyString_FromString.c 2008-12-14 17:03:53.945677523 +0800
+++ PyString_FromStringAndSize.c 2008-12-14 17:04:25.054637950 +0800
@@ -1,14 +1,10 @@
PyObject *
-PyString_FromString(const char *str)
+PyString_FromStringAndSize(const char *str, Py_ssize_t size)
{
- register size_t size;
register PyStringObject *op;
-
- assert(str != NULL);
- size = strlen(str);
- if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) {
- PyErr_SetString(PyExc_OverflowError,
- "string is too long for a Python string");
+ if (size < 0) {
+ PyErr_SetString(PyExc_SystemError,
+ "Negative size passed to PyString_FromStringAndSize");
return NULL;
}
if (size == 0 && (op = nullstring) != NULL) {
@@ -18,7 +14,9 @@
Py_INCREF(op);
return (PyObject *)op;
}
- if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
+ if (size == 1 && str != NULL &&
+ (op = characters[*str & UCHAR_MAX]) != NULL)
+ {
#ifdef COUNT_ALLOCS
one_strings++;
#endif
@@ -26,6 +24,11 @@
return (PyObject *)op;
}
+ if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) {
+ PyErr_SetString(PyExc_OverflowError, "string is too large");
+ return NULL;
+ }
+
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
if (op == NULL)
@@ -33,7 +36,9 @@
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
- Py_MEMCPY(op->ob_sval, str, size+1);
+ if (str != NULL)
+ Py_MEMCPY(op->ob_sval, str, size);
+ op->ob_sval[size] = '\ 0';
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
@@ -41,7 +46,7 @@
op = (PyStringObject *)t;
nullstring = op;
Py_INCREF(op);
- } else if (size == 1) {
+ } else if (size == 1 && str != NULL) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
我们把PyString_FromString称为A, PyString_FromStringAndSize称为B. 从diff的结果
分析, 我们可以知道:
1. A用assert假设str不为NULL; B接受str为NULL的情况.
2. A的用strlen计算size, size必然非负; B对于传入的size为负数的情况, 抛出异常,
类型为PyExc_SystemError.
3. A对于字符串str, 要求以'\ 0'结尾, 而且中间不能包含'\ 0'; B对于字符串str, 并不
要求以'\ 0'结尾, 且允许字符串中间包含'\ 0', 但要求字符串必须至少含有size个字符.
看看简化之后的代码:
PyObject *
PyString_FromStringAndSize(const char *str, Py_ssize_t size)
{
register PyStringObject *op;
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
if (str != NULL)
Py_MEMCPY(op->ob_sval, str, size);
op->ob_sval[size] = '\ 0';
return (PyObject *) op;
}
不过就是size不需要自己计算, 改为传入了而已. 不过这一小小的变化, 却导致这两个
函数可以接受的字符串性质的不同.
[注]该死的Ycool, 代码大概有问题, 会把\ 0(中间空格去掉)替换成#CONTENT#....
Python源码剖析学习 - Chap3 - Python中的字符串对象 - Part 1
Lee.MaRS 发表于 2008-12-03 14:02:12
望对Python有爱的筒子都能购买一本, 支持这本国内难得一见的好书.
转载本文, 请注明作者leemars. 欢迎大家访问作者的博客 http://leemars.ycool.com
[前言]
在Chap2中, 我们对PyIntObject进行了分析. 还记得PyIntObject是如何分类的么?
PyIntObject是定长对象(继承自PyObject), 以及不可变对象.
在Chap3中, 我们将对Python的另一个内建对象PyStringObject进行分析. 我们将看到,
Python的内建字符串对象PyStringObject是变长对象, 以及不可变对象.
[PyStringObject对象是不可变对象?]
是的. 这代表PyStringObject对象一但创建, 其维护的字符串值就不会被更改. 你对这
个对象的一切修改, 都将以返回一个新的对象实例的方式完成, 就和PyIntObject一样.
对于习惯C/C++的人来说, 这一点可能很难理解, 因为在C中, 字符串就是字符指针指向
一段空间(字符数组亦是类似), 在C++中, STL的string所维护的值也是可以改变的, 对
string的修改并不会产生新的实例.
不过在Java中, String对象就是不可变对象. 同时, Java也有类似的intern机制, 有兴
趣的筒子可以Google一下看看.
字符串(对象)是可变还是不可变, 我个人觉得只是一个设计哲学的问题. 可变的字符串
在处理字符串操作时可以获得更高的效率(因为不需要产生大量的临时对象); 不可变的
字符串则能让编译器可以共享字符串, 同时多线程访问字符串时不会有问题(因为对象不
可变). 关于这个问题, 在网上有很多的讨论, 大家Google一下. (被打飞)
(爬回来...)顺便说, Ruby的String对象是可变对象. (再次被打飞)
[PyStringObject对象]
废话不多说, 直接在Source Navigator里面查看PyStringObject的定义:
[Include/stringobject.h]
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
依次看下来:
1. PyObject_VAR_HEAD.
说明PyStringObject是变长对象(继承自PyVarObject).
2. ob_shash
注释说了, 这个是用来存放字符串的hash值的(-1表示未计算).
以后在分析Python的dict对象PyDictObject时会看到, 这个hash值将发挥巨大的作用.
3. ob_sstate
ob_sstate值不为0, 当且仅当该字符串对象在interned字典内. 在这种情况下,
interned字典对该对象的两次引用不计在引用计数内.
字符串对象的intern机制将在后面分析.
4. ob_sval
注释说了, ob_sval包含了ob_size+1个元素, 且ob_sval[ob_size]为0(即'#CONTENT#').
前面刚说过, PyStringObject为变长对象. 这里就体现了变长对象的特点: ob_size域.
同C中的字符串一样, PyStringObject包含的字符串必须以'#CONTENT#'结尾. 但是Python允许字
符串中间出现'#CONTENT#', 所以PyStringObject用ob_size域指指示字符串的长度(如"Python",
ob_size就是6).
回到ob_sval. ob_sval看起来是一个只有1个字符的数组. 不过在C中, 从来没有边界检
查, 想越界就越界. 只要给PyStringObject申请空间时, 分配额外的空间(比如, 申请大
小为sizeof(PyStringObject) + ob_size的空间), 由于ob_sval在结构体的尾部, 往
ob_sval写入字符串时, 越界的部分也恰好在申请的空间内. 长度为ob_size的字符串,
需要ob_size + 1的空间, 大小正好是ob_sval自带的1, 加上额外申请的ob_size.
好学爱问的筒子总是会有提出问题的:
1. 为什么不直接用字符指针?
多麻烦啊, 还要自己初始化指针的指向.
2. 为什么不用大小为0的数组?
恩, 知道大小为0的数组, 说明你很不错(被揍..). 不过这个特性并不是所有的编译器都
支持. 再说了, 我们需要的本来就是ob_size + 1的空间, 直接把1空间放在这里, 不是
非常合适么?
[PyString_Type对象]
二话不说继续贴代码:
[Objects/stringobject.c]
PyTypeObject PyString_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"str", /* tp_name */
sizeof(PyStringObject), /* tp_basicsize */
sizeof(char), /* tp_itemsize */
string_dealloc, /* tp_dealloc */
(printfunc)string_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
string_repr, /* tp_repr */
&string_as_number, /* tp_as_number */
&string_as_sequence, /* tp_as_sequence */
&string_as_mapping, /* tp_as_mapping */
(hashfunc)string_hash, /* tp_hash */
0, /* tp_call */
string_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
&string_as_buffer, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_STRING_SUBCLASS |
Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
string_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)string_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
string_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&PyBaseString_Type, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
string_new, /* tp_new */
PyObject_Del, /* tp_free */
};
对照书本. 我们会看到头部的初始化宏已经改为了PyVarObject_HEAD_INIT. 我们在之前
的PEP 3123中已经解释过了这个问题. 同时, tp_itemsize域被设为了sizeof(char), 这
表示该变长对象的变长部分, 每个元素的大小是sizeof(char). 结合ob_size域表示该变
长对象的变长部分有几个元素, ob_size * tp_itemsize即为变长部分的大小.
另外注意到, 在PyString_Type中, tp_as_number, tp_as_sequence, tp_as_mapping三
个域都被赋值了. 这说明, PyStringObject对于数值操作, 序列操作, 映射操作都支持.
我们来看看PyStringObject都怎么支持这些操作的:
[Objects/stringobject.c]
static PyNumberMethods string_as_number = {
0, /*nb_add*/
0, /*nb_subtract*/
0, /*nb_multiply*/
0, /*nb_divide*/
string_mod, /*nb_remainder*/
};
static PySequenceMethods string_as_sequence = {
(lenfunc)string_length, /*sq_length*/
(binaryfunc)string_concat, /*sq_concat*/
(ssizeargfunc)string_repeat, /*sq_repeat*/
(ssizeargfunc)string_item, /*sq_item*/
(ssizessizeargfunc)string_slice, /*sq_slice*/
0, /*sq_ass_item*/
0, /*sq_ass_slice*/
(objobjproc)string_contains /*sq_contains*/
};
static PyMappingMethods string_as_mapping = {
(lenfunc)string_length,
(binaryfunc)string_subscript,
0,
};
啊哈, 来看看我们熟悉的字符串特性都是怎么挂上去的:
1. string的格式代入. 记得"a = %d, b = %d" % (a,b)这样的特性么? 完成这个任务的
就是被挂在nb_remainder上的string_mod函数.
2. string的重复. "abc" * 5, 就能得到"abcabcabcabcabc". 完成这个任务的就是被挂
在sq_repeat上的string_repeat函数.
3. string的下标. 字符串的下标操作实在是太常用了. 根据下标的不同, Python会调用
不同的钩子.
1) s[0], s[1], ... 这类的index下标. 使用string_subscript.
2) s[0:2], s[1:3], ... 这类的slice下标. 使用string_slice.
3) s[0:4:2], ... 这类的slice下标. 使用string_subscript.
为什么不同的下标, 会调用不同的钩子呢? 我现在也没有答案(被暴打..). 这个问题估
计要到后面分析到Python的操作调用时才会明白.
Python源码剖析学习 - Chap2 - Python的整数对象 - Part 3
Lee.MaRS 发表于 2008-12-02 00:10:41
我们之前提到过, PyIntObject是不可变对象. 如果不举个例子来看看, 可能还不理解这
是怎么回事. 接下来我们就来看个例子.
还记得Py*_Type中的三个重要的域么? 恩, 就是那tp_as_number, tp_as_sequence,
tp_as_mapping三大员. PyIntObject中只指定了tp_as_number为&int_as_number, 我们
看看int_as_number长啥样:
[Objects/intobject.c]
static PyNumberMethods int_as_number = {
(binaryfunc)int_add, /*nb_add*/
(binaryfunc)int_sub, /*nb_subtract*/
(binaryfunc)int_mul, /*nb_multiply*/
(binaryfunc)int_classic_div, /*nb_divide*/
(binaryfunc)int_mod, /*nb_remainder*/
(binaryfunc)int_divmod, /*nb_divmod*/
(ternaryfunc)int_pow, /*nb_power*/
(unaryfunc)int_neg, /*nb_negative*/
(unaryfunc)int_int, /*nb_positive*/
(unaryfunc)int_abs, /*nb_absolute*/
(inquiry)int_nonzero, /*nb_nonzero*/
(unaryfunc)int_invert, /*nb_invert*/
(binaryfunc)int_lshift, /*nb_lshift*/
(binaryfunc)int_rshift, /*nb_rshift*/
(binaryfunc)int_and, /*nb_and*/
(binaryfunc)int_xor, /*nb_xor*/
(binaryfunc)int_or, /*nb_or*/
int_coerce, /*nb_coerce*/
(unaryfunc)int_int, /*nb_int*/
(unaryfunc)int_long, /*nb_long*/
(unaryfunc)int_float, /*nb_float*/
(unaryfunc)int_oct, /*nb_oct*/
(unaryfunc)int_hex, /*nb_hex*/
0, /*nb_inplace_add*/
0, /*nb_inplace_subtract*/
0, /*nb_inplace_multiply*/
0, /*nb_inplace_divide*/
0, /*nb_inplace_remainder*/
0, /*nb_inplace_power*/
0, /*nb_inplace_lshift*/
0, /*nb_inplace_rshift*/
0, /*nb_inplace_and*/
0, /*nb_inplace_xor*/
0, /*nb_inplace_or*/
(binaryfunc)int_div, /* nb_floor_divide */
int_true_divide, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
(unaryfunc)int_int, /* nb_index */
};
注意到int_as_number中提供的操作中, nb_inplace_*系列都没有赋值. 我们学过英文都
知道, inplace就是"原地"的意思啦(被打), 也就是说, PyIntObject并没有提供对对象
自身进行修改的操作.
抓int_add出来看看:
[Objects/intobject.c]
#define CONVERT_TO_LONG(obj, lng) \
if (PyInt_Check(obj)) { \
lng = PyInt_AS_LONG(obj); \
} \
else { \
Py_INCREF(Py_NotImplemented); \
return Py_NotImplemented; \
}
[Objects/intobject.c]
static PyObject *
int_add(PyIntObject *v, PyIntObject *w)
{
register long a, b, x;
CONVERT_TO_LONG(v, a);
CONVERT_TO_LONG(w, b);
x = a + b;
if ((x^a) >= 0 || (x^b) >= 0)
return PyInt_FromLong(x);
return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);
}
看到了么? Python中两个PyIntObject相加是这么做的: 将v的值取到a中, 将w的值取到b
中, a和b相加得到x, 若没有溢出, 则从x生成一个PyIntObject并返回, 否则返回调用大
整数对象类型的加法对v和w相加的结果.
为什么要将PyIntObject实现为不可变对象? 考虑到我们对于小整数进行了优化, 实现了
小整数对象的共享. 如果PyIntObject是可变对象, 在实现小整数对象的共享时就会遇到
许多的困难.

