Skip to content

Instantly share code, notes, and snippets.

@ruoyu0088
Created June 4, 2017 04:29
Show Gist options
  • Save ruoyu0088/e52bbb2afd186bd0b1ff72ebbee14869 to your computer and use it in GitHub Desktop.
Save ruoyu0088/e52bbb2afd186bd0b1ff72ebbee14869 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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