Skip to content

Instantly share code, notes, and snippets.

@kne42
Last active February 26, 2019 19:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kne42/522c2dd5d55d9c7034d1d62d7af2b357 to your computer and use it in GitHub Desktop.
Save kne42/522c2dd5d55d9c7034d1d62d7af2b357 to your computer and use it in GitHub Desktop.
fancy lists
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"class List(list):\n",
" \"\"\"Inheritable list that better connects related/dependent functions,\n",
" allowing for an easier time making modifications with reusable components.\n",
" \n",
" It has the following new methods:\n",
" `__locitem__(key)` : transform a key into the index of its corresponding item\n",
" `__prsitem__(key)` : parse a key such as `0:1` into indices\n",
" `__newlike__(iterable)` : create a new instance given an iterable\n",
" \n",
" TODO: handle operators (e.g. +, *, etc.)\n",
" \"\"\"\n",
" def __contains__(self, key):\n",
" try:\n",
" self.index(key)\n",
" return True\n",
" except ValueError:\n",
" return False\n",
" \n",
" def __iter__(self):\n",
" for i in range(len(self)):\n",
" yield self[i]\n",
" \n",
" def __getitem__(self, key):\n",
" indices = self.__prsitem__(key)\n",
" can_iter = hasattr(indices, '__iter__')\n",
" \n",
" if can_iter:\n",
" return self.__newlike__(super(List, self).__getitem__(i) for i in indices)\n",
" \n",
" return super().__getitem__(indices)\n",
" \n",
" def __setitem__(self, key, value):\n",
" super().__setitem__(self.__locitem__(key), value)\n",
" \n",
" def __delitem__(self, key):\n",
" super().__delitem__(self.__locitem__(key))\n",
" \n",
" def __prsitem__(self, key):\n",
" \"\"\"Parse a key into list indices.\n",
" \n",
" Default implementation handles slices\n",
" \n",
" Parameters\n",
" ----------\n",
" key : any\n",
" Key to parse.\n",
" \n",
" Returns\n",
" -------\n",
" indices : int or iterable of int\n",
" Key parsed into indices.\n",
" \"\"\"\n",
" if isinstance(key, slice):\n",
" start = key.start\n",
" stop = key.stop\n",
" step = key.step\n",
" \n",
" if start is not None:\n",
" try:\n",
" start = self.__locitem__(start)\n",
" except IndexError:\n",
" if start != len(self):\n",
" raise\n",
" \n",
" if stop is not None:\n",
" try:\n",
" stop = self.__locitem__(stop)\n",
" except IndexError:\n",
" if stop != len(self):\n",
" raise\n",
" \n",
" return range(*slice(start, stop, step).indices(len(self)))\n",
" else:\n",
" return self.__locitem__(key)\n",
" \n",
" def __locitem__(self, key):\n",
" \"\"\"Parse a key into a list index.\n",
" \n",
" Default implementation handles integers.\n",
" \n",
" Parameters\n",
" ----------\n",
" key : any\n",
" Key to parse.\n",
" \n",
" Returns\n",
" -------\n",
" index : int\n",
" Location of the object ``key`` is referencing.\n",
" \n",
" Raises\n",
" ------\n",
" IndexError\n",
" When the index is out of bounds.\n",
" KeyError\n",
" When the key is otherwise invalid.\n",
" \"\"\" \n",
" if not isinstance(key, int):\n",
" raise TypeError(f'expected int; got {type(key)}')\n",
" \n",
" if key < 0:\n",
" key += len(self)\n",
" \n",
" if not (0 <= key < len(self)):\n",
" raise IndexError(f'expected index to be in [0, {len(self)}); got {key}')\n",
" \n",
" return key\n",
" \n",
" def __newlike__(self, iterable):\n",
" \"\"\"Create a new instance from an iterable with the same properties as this one.\n",
" \n",
" Parameters\n",
" ----------\n",
" iterable : iterable\n",
" Elements to make the new list from.\n",
" \n",
" Returns\n",
" -------\n",
" new_list : List\n",
" New list created from the iterable with the same properties.\n",
" \"\"\"\n",
" cls = type(self)\n",
" return cls(iterable)\n",
" \n",
" def copy(self):\n",
" return self.__newlike__(self)\n",
" \n",
" def count(self, key):\n",
" super().count(self.__locitem__(key))\n",
" \n",
" def extend(self, iterable):\n",
" for e in iterable:\n",
" self.append(e)\n",
" \n",
" def insert(self, index, object):\n",
" super().insert(self.__locitem__(index), object)\n",
" \n",
" def pop(self, index):\n",
" super().pop(self.__locitem__(index))\n",
" \n",
" def remove(self, object):\n",
" self.pop(self.__locitem__(object))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"l1 = [1, 2, 3]\n",
"l2 = List(l1)\n",
"\n",
"class compare:\n",
" def __getitem__(self, key):\n",
" display(l1[key])\n",
" display(l2[key])\n",
" \n",
"l = compare()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"1"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[0]"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[1]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[0:1]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2, 3]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[2, 3]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[1:]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[2]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[1:-1]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[3, 2, 1]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[3, 2, 1]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[::-1]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[1]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[0::-1]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"3"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[-1]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2, 1]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[2, 1]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[1::-1]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[3, 2]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[3, 2]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[3:0:-1]"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[1, 2, 3]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"l[:]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3]\n"
]
}
],
"source": [
"print('[' + ', '.join(str(e) for e in l2) + ']')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"def cpprint(obj):\n",
" return obj.__name__"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"class TypedList(List):\n",
" \"\"\"Enforce list elements to be of a specific type and allow indexing with their unique properties.\n",
" \n",
" Parameters\n",
" ----------\n",
" basetype : type\n",
" Type of the elements in the list.\n",
" iterable : iterable, optional\n",
" Elements to initialize the list with.\n",
" lookup : dict of type : function(object, ``basetype``) -> bool\n",
" Functions that determine if an object is a reference to an\n",
" element of the list.\n",
" \"\"\"\n",
" def __init__(self, basetype, iterable=(), lookup={}):\n",
" self._basetype = basetype\n",
" self._lookup = lookup\n",
" super().__init__(self._check(e) for e in iterable)\n",
" \n",
" def __setitem__(self, key, value):\n",
" self._check(value)\n",
" super().__setitem__(key, value)\n",
" \n",
" def __locitem__(self, key):\n",
" if not isinstance(key, int):\n",
" key = self.index(key)\n",
" return super().__locitem__(key)\n",
" \n",
" def __newlike__(self, iterable):\n",
" cls = type(self)\n",
" return cls(self._basetype, iterable, self._lookup)\n",
" \n",
" def _check(self, e):\n",
" assert isinstance(e, self._basetype)\n",
" return e\n",
" \n",
" def insert(self, key, object):\n",
" self._check(object)\n",
" super().insert(key, object)\n",
" \n",
" def append(object):\n",
" self._check(object)\n",
" super().append(object)\n",
" \n",
" def index(self, value, start=None, stop=None):\n",
" q = value\n",
" basetype = self._basetype\n",
" \n",
" if not isinstance(q, basetype):\n",
" lookup = self._lookup\n",
" \n",
" for t in lookup:\n",
" if isinstance(q, t):\n",
" break\n",
" else:\n",
" raise TypeError(f'expected object of type {cpprint(basetype)} '\n",
" f'or one of {set(cpprint(t) for t in lookup)}; '\n",
" f'got {cpprint(type(q))}')\n",
" \n",
" ref = lookup[t]\n",
" \n",
" for e in self[start:stop]:\n",
" if ref(q, e):\n",
" break\n",
" else:\n",
" raise KeyError(f'could not find element {q} was referencing')\n",
" \n",
" q = e\n",
" \n",
" return super().index(q)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"l = TypedList(float, [1.2, 4.2], {str: lambda q, e: float(q) == e})"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.2"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l['1.2']"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4.2"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l['4.2']"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1.2, 4.2]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l['1.2':]"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1.2]"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l['1.2'::-1]"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1.2]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[1.2, 4.2][0::-1]"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1.2]"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"List([1.2, 4.2])[0::-1]"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"class MultiIndexList(List):\n",
" \"\"\"Allow indexing with tuples.\n",
" \"\"\"\n",
" def __prsitem__(self, keys):\n",
" if not isinstance(keys, tuple):\n",
" return super().__prsitem__(keys)\n",
" \n",
" indices = []\n",
" \n",
" for key in keys:\n",
" i = self.__prsitem__(key)\n",
" can_iter = hasattr(i, '__iter__')\n",
" if can_iter:\n",
" indices.extend(i)\n",
" else:\n",
" indices.append(i)\n",
" \n",
" # TODO: how to handle duplicates?\n",
" \n",
" return indices\n",
" \n",
" def __setitem__(self, key, value):\n",
" indices = self.__prsitem__(key)\n",
" can_iter = hasattr(indices, '__iter__')\n",
" \n",
" if can_iter:\n",
" if hasattr(value, '__getitem__') and hasattr(value, '__len__'):\n",
" if len(value) not in (1, len(indices)):\n",
" raise ValueError(f'expected values of length 1 or {len(indices)}; '\n",
" f'got {len(value)}')\n",
" for o, i in enumerate(indices):\n",
" super().__setitem__(i, value[o])\n",
" else:\n",
" for i in indices:\n",
" super().__setitem__(i, value)\n",
" else:\n",
" super().__setitem__(indices, value)\n",
" \n",
" def __delitem__(self, key):\n",
" can_iter, indices = self.__prsitem__(key)\n",
" \n",
" if can_iter:\n",
" for i in indices:\n",
" super().__delitem__(i)\n",
" else: \n",
" super().__delitem__(indices)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"l = MultiIndexList('abcdefghijklmnopqrstuvwxyz')"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'z'"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l[-1]"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['a', 'c']"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l[0, 2]"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['z', 'a', 'b', 'c']"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l[-1, 0:3]"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[5,\n",
" 5,\n",
" 5,\n",
" 'd',\n",
" 'e',\n",
" 'f',\n",
" 'g',\n",
" 'h',\n",
" 'i',\n",
" 'j',\n",
" 'k',\n",
" 'l',\n",
" 'm',\n",
" 'n',\n",
" 'o',\n",
" 'p',\n",
" 'q',\n",
" 'r',\n",
" 's',\n",
" 't',\n",
" 'u',\n",
" 'v',\n",
" 'w',\n",
" 'x',\n",
" 'y',\n",
" 5]"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l2 = l.copy()\n",
"l2[-1, 0:3] = 5\n",
"l2"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['E',\n",
" 'b',\n",
" 'c',\n",
" 'F',\n",
" 'e',\n",
" 'G',\n",
" 'g',\n",
" 'h',\n",
" 'i',\n",
" 'j',\n",
" 'k',\n",
" 'l',\n",
" 'm',\n",
" 'n',\n",
" 'o',\n",
" 'p',\n",
" 'q',\n",
" 'r',\n",
" 's',\n",
" 't',\n",
" 'u',\n",
" 'v',\n",
" 'w',\n",
" 'x',\n",
" 'y',\n",
" 'z']"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l3 = l.copy()\n",
"l3[0, 3, 5] = 'EFG'\n",
"l3"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
"from vispy.util.event import EventEmitter\n",
"\n",
"\n",
"class EventfulList(List):\n",
" \"\"\"Notify on content change.\n",
" \"\"\"\n",
" def __init__(self, iterable=()):\n",
" super().__init__(iterable)\n",
" self.changed = EventEmitter(source=self, type='changed')\n",
"\n",
" def __setitem__(self, index, value):\n",
" super().__setitem__(index, value)\n",
" self.changed()\n",
"\n",
" def insert(self, index, object):\n",
" super().insert(index, object)\n",
" self.changed()\n",
" \n",
" def append(self, object):\n",
" super().append(object)\n",
" self.changed()"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"l = EventfulList([0, 1, 2])"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<function __main__.<lambda>(e)>"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l.changed.connect(lambda e: print(e.source))"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[5, 1, 2]\n"
]
}
],
"source": [
"l[0] = 5"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[5, 1, 2, 42]\n"
]
}
],
"source": [
"l.append(42)"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[5, 1, 2, 42, 52]\n"
]
}
],
"source": [
"l.extend([52])"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"class TestEventfulMulti(EventfulList, MultiIndexList): pass"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
"l = TestEventfulMulti([1, 2, 3, 4, 5])"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<function __main__.<lambda>(e)>"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l.changed.connect(lambda e: print(e.source))"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[4, 2, 3, 1, 5]\n"
]
}
],
"source": [
"l[0, 3] = l[3, 0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:napari-gui]",
"language": "python",
"name": "conda-env-napari-gui-py"
},
"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.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment