Created
June 4, 2017 04:29
-
-
Save ruoyu0088/e52bbb2afd186bd0b1ff72ebbee14869 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# 让`deque`支持切片下标" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import cffi\n", | |
"from collections import deque" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Python的标准模块`collections`中的`deque`是一个双向链表结构,它支持在该链表的左右添加元素或者删除元素。它还支持整数下标运算,但是它不支持切片下标:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"9\n" | |
] | |
}, | |
{ | |
"ename": "TypeError", | |
"evalue": "sequence index must be integer, not 'slice'", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-5-1d9676b37db5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;31mTypeError\u001b[0m: sequence index must be integer, not 'slice'" | |
] | |
} | |
], | |
"source": [ | |
"d = deque(range(10))\n", | |
"\n", | |
"print(d[-1])\n", | |
"print(d[-3:])" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"使用`itertools`模块中的`slice()`可以获得对指定的切片迭代的对象,然后再调用`list()`将迭代器转换为列表即可:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"[7, 8, 9]" | |
] | |
}, | |
"execution_count": 13, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"from itertools import islice\n", | |
"\n", | |
"list(islice(d, len(d) - 3, len(d), 1))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"但是`islice()`不支持负数下标,可以使用`slice`对象的`indices()`方法将一个切片对象转换为`start`、`end`和`step`三个整数值。例如:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"[7, 8, 9]" | |
] | |
}, | |
"execution_count": 16, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"start, end, step = slice(-3, None).indices(len(d))\n", | |
"list(islice(d, start, end, step))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"如上所述,我们知道了如何使用切片获取`deque`对象的部分元素,下面让我们看看如何让`deque`支持切片下标。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"t = ffi.cast(\"ssize_t *\", id(d))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"[1, 140233362912128, 0, 140232829701184, 140232829701184, 32, 31, 0, -1, 0]" | |
] | |
}, | |
"execution_count": 9, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"list(t[0:10])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"dict_keys(['maxlen', '__ge__', '__contains__', 'copy', '__new__', 'extend', '__sizeof__', '__hash__', 'rotate', '__getattribute__', '__copy__', '__lt__', '__iadd__', 'reverse', '__repr__', 'append', '__bool__', '__rmul__', 'extendleft', '__delitem__', '__reversed__', '__setitem__', '__eq__', '__le__', '__iter__', '__mul__', 'appendleft', 'popleft', 'pop', '__init__', '__len__', '__add__', 'insert', 'remove', '__imul__', 'index', '__doc__', 'clear', '__getitem__', '__ne__', '__reduce__', '__gt__', 'count'])\n" | |
] | |
} | |
], | |
"source": [ | |
"print(deque.__dict__.keys())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 32, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"%load_ext cython" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 96, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"%%cython\n", | |
"from cpython.ref cimport PyObject\n", | |
"from cpython.sequence cimport PySequence_GetItem\n", | |
"from cpython.slice cimport PySlice_GetIndices\n", | |
"from itertools import islice\n", | |
"from collections import deque\n", | |
"DEF BLOCKLEN = 64\n", | |
"\n", | |
"cdef struct block:\n", | |
" block *leftlink\n", | |
" PyObject * data[BLOCKLEN]\n", | |
" block *rightlink\n", | |
"\n", | |
"cdef struct dequeobject:\n", | |
" ssize_t ob_refcnt\n", | |
" PyObject * ob_type\n", | |
" ssize_t ob_size\n", | |
" block *leftblock\n", | |
" block *rightblock\n", | |
" ssize_t leftindex\n", | |
" ssize_t rightindex\n", | |
" size_t state\n", | |
" ssize_t maxlen\n", | |
" PyObject * weakreflist\n", | |
"\n", | |
"cdef struct PyMappingMethods:\n", | |
" void * mp_length\n", | |
" void * mp_subscript\n", | |
" void * mp_ass_subscript\n", | |
"\n", | |
"cdef ssize_t deque_len(dequeobject * dq):\n", | |
" return dq.ob_size\n", | |
"\n", | |
"cdef deque_subscript(dequeobject * dq, object item):\n", | |
" cdef ssize_t start, end, step\n", | |
" if isinstance(item, int):\n", | |
" return PySequence_GetItem(<object>dq, <ssize_t>item)\n", | |
" elif isinstance(item, slice):\n", | |
" PySlice_GetIndices(item, dq.ob_size, &start, &end, &step)\n", | |
" return deque(islice(<object>dq, start, end, step))\n", | |
"\n", | |
"cdef PyMappingMethods deque_mapping_methods = [<void *>deque_len, <void *>deque_subscript, NULL]\n", | |
"\n", | |
"def addr_of_mapping_methods():\n", | |
" return <ssize_t>&(deque_mapping_methods)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 97, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"4\n", | |
"deque([2, 3, 4])\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"/usr/local/anaconda/envs/py35/lib/python3.5/site-packages/cffi/vengine_cpy.py:188: UserWarning: reimporting '_cffi__xc07c2ec2x260339d1' might overwrite older definitions\n", | |
" % (self.verifier.get_module_name()))\n" | |
] | |
} | |
], | |
"source": [ | |
"ffi = cffi.FFI()\n", | |
"\n", | |
"ffi.cdef(\"\"\"\n", | |
"ssize_t tp_as_sequence_offset;\n", | |
"ssize_t sq_slice_offset;\n", | |
"ssize_t mapping_methods_offset;\n", | |
"\"\"\")\n", | |
"\n", | |
"lib = ffi.verify(\"\"\"\n", | |
"\n", | |
"ssize_t tp_as_sequence_offset = offsetof(PyTypeObject, tp_as_sequence);\n", | |
"ssize_t sq_slice_offset = offsetof(PySequenceMethods, was_sq_slice);\n", | |
"ssize_t mapping_methods_offset = offsetof(PyTypeObject, tp_as_mapping);\n", | |
"\"\"\", tmpdir=\"/usr/local/home/tmp\")\n", | |
"\n", | |
"tp_as_mapping = ffi.cast(\"ssize_t *\", id(deque) + lib.mapping_methods_offset)\n", | |
"\n", | |
"tp_as_mapping[0] = addr_of_mapping_methods()\n", | |
"\n", | |
"a = deque([1,2,3,4])\n", | |
"\n", | |
"print(a[3])\n", | |
"print(a[1:4])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 98, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"a.append(5)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 99, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"deque([4, 5])" | |
] | |
}, | |
"execution_count": 99, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"a[-2:]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 27, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"0" | |
] | |
}, | |
"execution_count": 27, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"ffi.cast(\"ssize_t *\", addr + lib.sq_slice_offset)[0]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 36, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def f():\n", | |
" a = deque([1,2,3])\n", | |
" b = a[2:]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 37, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from dis import dis" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 79, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"slice.indices?" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"anaconda-cloud": {}, | |
"kernelspec": { | |
"display_name": "Python [default]", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.5.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment