网易游戏面试经验(一)

前言

最近一直没有写博客,在忙着准备申请各大公司的实习,现在基本已经定下来了,特将这段时间面试中遇到的问题进行总结和解答,主要包括一些网络、算法、操作系统、python的问题,这些问题并不是以往比较常见的题目,更多的都是更加细节的或者以往没有见到的题目。文章更多的是面向技术,而不是面经,同时关于问题的解答都是自己一家之言,如有问题,还望大家指正。本文的知识点是关于python中的可变对象与不可变对象,面试官问我在python中是如何实现的。

正文

我最开始想到的是这个问题要考察的是python的内部实现机制,所以先简述下python的可变对象和不可变对象,再从python内部实现的角度分析下这个题目。
众所周知,python在堆中分配的对象按照是否可变分为两类:可变对象和不可变对象。关于他们各自的定义我想大家比较熟悉,特复制一段官方解释如下:In general, data types in Python can be distinguished based on whether objects of the type are mutable or immutable. The content of objects of immutable types cannot be changed after they are created. Only mutable objects support methods that change the object in place, such as reassignment of a sequence slice下面列举出一些常见的可变对象和不可变对象。

  • 可变对象:list, dictionary, set, byte array
  • 不可变对象:int,long, complex, string, float, tuple, frozen set

我们用一个很简单的例子来看看可变对象和不可变对象。

# code
a = [1, 2, 3]
a[1] = 4
print a
b = "123"
b[1] = '4'
print b
# output
[1, 4, 3]
Traceback (most recent call last):
  File "test.py", line 5, in <module>
    b[1] = '4'
TypeError: 'str' object does not support item assignment

在上述例子中,list中的第二个元素可以被改变,但是如果试图改变string中的第二个元素,就会报错。但是有人可能有如下疑惑,比如下面的代码:

# code
a = 1
print a
a = 2
print a
# output
1
2

有些人觉得上面的代码中a为一个int型的对象,但是他的值发生了变化,而解释器也没有报错。这里有必要提一下python的赋值语句,在python中,赋值语句其实是建立对对象的引用值,而不是复制对象,即更像是指针。所以在语句a=1执行后,只是建立了一个引用值a来指向int对象1,所以当再执行a=2这个语句的时候,是将这个引用值指向了int对象2,所以这里并没有改变int对象。
既然现在明白了python中的可变对象和不可变对象,那么在python中他们分别是如何实现的呢?
这个问题的答案我觉得很简单,如果对象中定义了修改对象的成员方法,则这个对象是可变的,否则是不可变对象,所以我们可以通过修改python的源码让String也变成一个可变对象。这个可以从python源码中得到证实,比如下面的python代码:a = [1,2] a[0]=3就调用了下面的函数:

int
PyList_SetItem(PyObject *op, Py_ssize_t i,
               PyObject *newitem)
{
    PyObject **p;
    if (!PyList_Check(op)) {
        Py_XDECREF(newitem);
        PyErr_BadInternalCall();
        return -1;
    }
    if (i < 0 || i >= Py_SIZE(op)) {
        Py_XDECREF(newitem);
        PyErr_SetString(PyExc_IndexError,
                        "list assignment index out of range");
        return -1;
    }
    p = ((PyListObject *)op) -> ob_item + i;
    Py_SETREF(*p, newitem);
    return 0;
}

在上面的代码中,首先会进行类型检查,随后进行索引的有效性检查,当类型检查和索引有效性检查通过之后,将待加入的指针放到指定的位置。这是设置元素,常用的还有插入元素,代码如下:

static PyObject *
listinsert(PyListObject *self, PyObject *args)
{
    Py_ssize_t i;
    PyObject *v;
    if (!PyArg_ParseTuple(args, "nO:insert", &i, &v))
        return NULL;
    if (ins1(self, i, v) == 0)
        Py_RETURN_NONE;
    return NULL;
}

关于list中的成员属性,都可以在源代码中看到,下面只列举出在python中的方法名和python源码中的实现函数的对象关系:

static PyMethodDef list_methods[] = {
    {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, getitem_doc},
    {"__reversed__",(PyCFunction)list_reversed, METH_NOARGS, reversed_doc},
    {"__sizeof__",  (PyCFunction)list_sizeof, METH_NOARGS, sizeof_doc},
    {"clear",           (PyCFunction)listclear,   METH_NOARGS, clear_doc},
    {"copy",            (PyCFunction)listcopy,   METH_NOARGS, copy_doc},
    {"append",          (PyCFunction)listappend,  METH_O, append_doc},
    {"insert",          (PyCFunction)listinsert,  METH_VARARGS, insert_doc},
    {"extend",          (PyCFunction)listextend,  METH_O, extend_doc},
    {"pop",             (PyCFunction)listpop,     METH_VARARGS, pop_doc},
    {"remove",          (PyCFunction)listremove,  METH_O, remove_doc},
    {"index",           (PyCFunction)listindex,   METH_VARARGS, index_doc},
    {"count",           (PyCFunction)listcount,   METH_O, count_doc},
    {"reverse",         (PyCFunction)listreverse, METH_NOARGS, reverse_doc},
    {"sort",            (PyCFunction)listsort,    METH_VARARGS | METH_KEYWORDS, sort_doc},
    {NULL,              NULL}           /* sentinel */
};

总结

我也想过,这个题目可能更想考察的是python的内存管理机制和对象机制,这部门的内容也曾经被单独问到过,所以以后会专门写一篇博客来讲述python的内存管理机制。
python的实现包含了很多智慧,用自己粗浅的理解来加深对python的认识和理解。

posted @ 2016-03-29 21:37  cotyb  阅读(3288)  评论(0编辑  收藏  举报
AmazingCounters.com