火星日记 ~ 第一千二百二十四天 ~ 半年的终结符

Lee.MaRS 发表于 2009-03-31 18:56:03

从2008.9到2009.3, 整整7个月的时间.

经历波澜起伏.

经历漫长等待.

终于等到了.

幻想的破灭.

赶在这三月的最后一天.

丝毫不用怀疑这可能是个April Fool的玩笑.

Reject @ Google, SET

Farewell, Google.
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

火星日记 ~ 第一千一百三十四天 ~ Happy new year!

Lee.MaRS 发表于 2008-12-31 22:58:31

让霉运连连的2008年赶紧滚蛋,迎接好运不断的2009年!

祝大家新年快乐,心想事成!

Python源码剖析读书笔记可能还会暂停一段时间,因为这项工作实在是非常耗费时间的一件事情,而我现在无法集中精力,静下来把笔记写上来。

不过这项工作还会继续下去,我现在也还在继续阅读这本书,在书上乱涂乱画。

希望大家在新的一年,还能把我的Blog留在你们的Google Reader里面。:-)
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

火星日记 ~ 第一千一百三十四天 ~ 人生第二封拒信

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$,注定是没有缘分的。

收藏: QQ书签 del.icio.us 订阅: Google 抓虾

火星日记 ~ 第一千一百二十八天 ~ Merry Christmas

Lee.MaRS 发表于 2008-12-25 02:20:29

转眼间圣诞节就到了.

圣诞夜没闲着, 参加合唱团的走穴活动去了.

四首歌, 唱了两次, 然后就搞定了...

收入两张大红纸...

唱得如何?

恩... 感觉唱得相当的囧...

不过现场的客人看上去根本不在乎你唱什么...

于是我也无所谓了...

反正明年就没我的份了 -.=b

上台唱的时候大家都很矜持.

反倒是在休息的时候唱得很high.

一堆人在那里拼命唱我不会的曲子, 郁...

害得我只好一个人在那里默默地看书...

圣诞夜看别人来吃天价自助餐, 其实心情有一点奇怪的.

zhouye小朋友说以后有钱了也要来这里吃, 再带个小MM...

我就囧住了...

我以后大概没什么机会来吃这种天价自助餐吧...

一是穷...

二是以我的勤俭持家的性格来看应该也不会做这样的事情...

最后离开酒店了以后, 一堆人在路口等着过马路时吃错药了开始唱 Ezekiel Saw The Wheel...

引来无数人侧目...

于是我们开始大声自称是交大的...

回来的时候坐在出租车上, 想想以后要是工作了, 一天的收入还没有一个晚上这样随便唱唱的收入多...

继续囧...
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Python源码剖析学习 - Chap3 - Python中的字符串对象 - Part2

Lee.MaRS 发表于 2008-12-14 20:06:00

本文是《Python源码剖析——深度探索动态语言核心技术》一书的摘录和读书笔记. 希
望对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#....
关键词(Tag): python python源码剖析
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Python源码剖析学习 - Chap3 - Python中的字符串对象 - Part 1

Lee.MaRS 发表于 2008-12-03 14:02:12

本文是《Python源码剖析——深度探索动态语言核心技术》一书的摘录和读书笔记. 希
望对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的操作调用时才会明白.
关键词(Tag): python python源码剖析
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Python源码剖析学习 - Chap2 - Python的整数对象 - Part 3

Lee.MaRS 发表于 2008-12-02 00:10:41

[PyIntObject是不可变对象?]
我们之前提到过, 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是可变对象, 在实现小整数对象的共享时就会遇到
许多的困难.
关键词(Tag): python python源码剖析
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

谁给推荐一个比较好用来做技术博客的空间吧?

Lee.MaRS 发表于 2008-11-30 00:04:31

对ycool有点忍无可忍了…
收藏: QQ书签 del.icio.us 订阅: Google 抓虾