mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
improve dict
and add lru_cache
This commit is contained in:
parent
e9fed99471
commit
0cb3684fa6
@ -7,6 +7,10 @@ label: functools
|
||||
|
||||
A decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated.
|
||||
|
||||
### `functools.lru_cache(maxsize=128)`
|
||||
|
||||
A decorator that wraps a function with a memoizing callable that saves up to the maxsize most recent calls.
|
||||
|
||||
### `functools.reduce(function, sequence, initial=...)`
|
||||
|
||||
Apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value. For example, `functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` calculates `((((1+2)+3)+4)+5)`. The left argument, `x`, is the accumulated value and the right argument, `y`, is the update value from the sequence. If the optional `initial` is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty.
|
||||
|
@ -735,7 +735,7 @@ enum py_PredefinedType {
|
||||
tp_locals,
|
||||
tp_code,
|
||||
tp_dict,
|
||||
tp_dict_items, // 1 slot
|
||||
tp_dict_iterator, // 1 slot
|
||||
tp_property, // 2 slots (getter + setter)
|
||||
tp_star_wrapper, // 1 slot + int level
|
||||
tp_staticmethod, // 1 slot
|
||||
|
@ -8,6 +8,26 @@ class cache:
|
||||
self.cache[args] = self.f(*args)
|
||||
return self.cache[args]
|
||||
|
||||
class lru_cache:
|
||||
def __init__(self, maxsize=128):
|
||||
self.maxsize = maxsize
|
||||
self.cache = {}
|
||||
|
||||
def __call__(self, f):
|
||||
def wrapped(*args):
|
||||
if args in self.cache:
|
||||
res = self.cache.pop(args)
|
||||
self.cache[args] = res
|
||||
return res
|
||||
|
||||
res = f(*args)
|
||||
if len(self.cache) >= self.maxsize:
|
||||
first_key = next(iter(self.cache))
|
||||
self.cache.pop(first_key)
|
||||
self.cache[args] = res
|
||||
return res
|
||||
return wrapped
|
||||
|
||||
def reduce(function, sequence, initial=...):
|
||||
it = iter(sequence)
|
||||
if initial is ...:
|
||||
|
@ -7,7 +7,7 @@ const char kPythonLibs_cmath[] = "import math\n\nclass complex:\n def __init_
|
||||
const char kPythonLibs_collections[] = "from typing import TypeVar, Iterable\n\ndef Counter[T](iterable: Iterable[T]):\n a: dict[T, int] = {}\n for x in iterable:\n if x in a:\n a[x] += 1\n else:\n a[x] = 1\n return a\n\n\nclass defaultdict(dict):\n def __init__(self, default_factory, *args):\n super().__init__(*args)\n self.default_factory = default_factory\n\n def __missing__(self, key):\n self[key] = self.default_factory()\n return self[key]\n\n def __repr__(self) -> str:\n return f\"defaultdict({self.default_factory}, {super().__repr__()})\"\n\n def copy(self):\n return defaultdict(self.default_factory, self)\n\n\nclass deque[T]:\n _data: list[T]\n _head: int\n _tail: int\n _capacity: int\n\n def __init__(self, iterable: Iterable[T] = None):\n self._data = [None] * 8 # type: ignore\n self._head = 0\n self._tail = 0\n self._capacity = len(self._data)\n\n if iterable is not None:\n self.extend(iterable)\n\n def __resize_2x(self):\n backup = list(self)\n self._capacity *= 2\n self._head = 0\n self._tail = len(backup)\n self._data.clear()\n self._data.extend(backup)\n self._data.extend([None] * (self._capacity - len(backup)))\n\n def append(self, x: T):\n self._data[self._tail] = x\n self._tail = (self._tail + 1) % self._capacity\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def appendleft(self, x: T):\n self._head = (self._head - 1) % self._capacity\n self._data[self._head] = x\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def copy(self):\n return deque(self)\n \n def count(self, x: T) -> int:\n n = 0\n for item in self:\n if item == x:\n n += 1\n return n\n \n def extend(self, iterable: Iterable[T]):\n for x in iterable:\n self.append(x)\n\n def extendleft(self, iterable: Iterable[T]):\n for x in iterable:\n self.appendleft(x)\n \n def pop(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n self._tail = (self._tail - 1) % self._capacity\n return self._data[self._tail]\n \n def popleft(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n x = self._data[self._head]\n self._head = (self._head + 1) % self._capacity\n return x\n \n def clear(self):\n i = self._head\n while i != self._tail:\n self._data[i] = None # type: ignore\n i = (i + 1) % self._capacity\n self._head = 0\n self._tail = 0\n\n def rotate(self, n: int = 1):\n if len(self) == 0:\n return\n if n > 0:\n n = n % len(self)\n for _ in range(n):\n self.appendleft(self.pop())\n elif n < 0:\n n = -n % len(self)\n for _ in range(n):\n self.append(self.popleft())\n\n def __len__(self) -> int:\n return (self._tail - self._head) % self._capacity\n\n def __contains__(self, x: object) -> bool:\n for item in self:\n if item == x:\n return True\n return False\n \n def __iter__(self):\n i = self._head\n while i != self._tail:\n yield self._data[i]\n i = (i + 1) % self._capacity\n\n def __eq__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n if len(self) != len(other):\n return False\n for x, y in zip(self, other):\n if x != y:\n return False\n return True\n \n def __ne__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n return not self == other\n \n def __repr__(self) -> str:\n return f\"deque({list(self)!r})\"\n\n";
|
||||
const char kPythonLibs_dataclasses[] = "def _get_annotations(cls: type):\n inherits = []\n while cls is not object:\n inherits.append(cls)\n cls = cls.__base__\n inherits.reverse()\n res = {}\n for cls in inherits:\n res.update(cls.__annotations__)\n return res.keys()\n\ndef _wrapped__init__(self, *args, **kwargs):\n cls = type(self)\n cls_d = cls.__dict__\n fields = _get_annotations(cls)\n i = 0 # index into args\n for field in fields:\n if field in kwargs:\n setattr(self, field, kwargs.pop(field))\n else:\n if i < len(args):\n setattr(self, field, args[i])\n i += 1\n elif field in cls_d: # has default value\n setattr(self, field, cls_d[field])\n else:\n raise TypeError(f\"{cls.__name__} missing required argument {field!r}\")\n if len(args) > i:\n raise TypeError(f\"{cls.__name__} takes {len(fields)} positional arguments but {len(args)} were given\")\n if len(kwargs) > 0:\n raise TypeError(f\"{cls.__name__} got an unexpected keyword argument {next(iter(kwargs))!r}\")\n\ndef _wrapped__repr__(self):\n fields = _get_annotations(type(self))\n obj_d = self.__dict__\n args: list = [f\"{field}={obj_d[field]!r}\" for field in fields]\n return f\"{type(self).__name__}({', '.join(args)})\"\n\ndef _wrapped__eq__(self, other):\n if type(self) is not type(other):\n return False\n fields = _get_annotations(type(self))\n for field in fields:\n if getattr(self, field) != getattr(other, field):\n return False\n return True\n\ndef _wrapped__ne__(self, other):\n return not self.__eq__(other)\n\ndef dataclass(cls: type):\n assert type(cls) is type\n cls_d = cls.__dict__\n if '__init__' not in cls_d:\n cls.__init__ = _wrapped__init__\n if '__repr__' not in cls_d:\n cls.__repr__ = _wrapped__repr__\n if '__eq__' not in cls_d:\n cls.__eq__ = _wrapped__eq__\n if '__ne__' not in cls_d:\n cls.__ne__ = _wrapped__ne__\n fields = _get_annotations(cls)\n has_default = False\n for field in fields:\n if field in cls_d:\n has_default = True\n else:\n if has_default:\n raise TypeError(f\"non-default argument {field!r} follows default argument\")\n return cls\n\ndef asdict(obj) -> dict:\n fields = _get_annotations(type(obj))\n obj_d = obj.__dict__\n return {field: obj_d[field] for field in fields}";
|
||||
const char kPythonLibs_datetime[] = "from time import localtime\nimport operator\n\nclass timedelta:\n def __init__(self, days=0, seconds=0):\n self.days = days\n self.seconds = seconds\n\n def __repr__(self):\n return f\"datetime.timedelta(days={self.days}, seconds={self.seconds})\"\n\n def __eq__(self, other) -> bool:\n if not isinstance(other, timedelta):\n return NotImplemented\n return (self.days, self.seconds) == (other.days, other.seconds)\n\n def __ne__(self, other) -> bool:\n if not isinstance(other, timedelta):\n return NotImplemented\n return (self.days, self.seconds) != (other.days, other.seconds)\n\n\nclass date:\n def __init__(self, year: int, month: int, day: int):\n self.year = year\n self.month = month\n self.day = day\n\n @staticmethod\n def today():\n t = localtime()\n return date(t.tm_year, t.tm_mon, t.tm_mday)\n \n def __cmp(self, other, op):\n if not isinstance(other, date):\n return NotImplemented\n if self.year != other.year:\n return op(self.year, other.year)\n if self.month != other.month:\n return op(self.month, other.month)\n return op(self.day, other.day)\n\n def __eq__(self, other) -> bool:\n return self.__cmp(other, operator.eq)\n \n def __ne__(self, other) -> bool:\n return self.__cmp(other, operator.ne)\n\n def __lt__(self, other: 'date') -> bool:\n return self.__cmp(other, operator.lt)\n\n def __le__(self, other: 'date') -> bool:\n return self.__cmp(other, operator.le)\n\n def __gt__(self, other: 'date') -> bool:\n return self.__cmp(other, operator.gt)\n\n def __ge__(self, other: 'date') -> bool:\n return self.__cmp(other, operator.ge)\n\n def __str__(self):\n return f\"{self.year}-{self.month:02}-{self.day:02}\"\n\n def __repr__(self):\n return f\"datetime.date({self.year}, {self.month}, {self.day})\"\n\n\nclass datetime(date):\n def __init__(self, year: int, month: int, day: int, hour: int, minute: int, second: int):\n super().__init__(year, month, day)\n # Validate and set hour, minute, and second\n if not 0 <= hour <= 23:\n raise ValueError(\"Hour must be between 0 and 23\")\n self.hour = hour\n if not 0 <= minute <= 59:\n raise ValueError(\"Minute must be between 0 and 59\")\n self.minute = minute\n if not 0 <= second <= 59:\n raise ValueError(\"Second must be between 0 and 59\")\n self.second = second\n\n def date(self) -> date:\n return date(self.year, self.month, self.day)\n\n @staticmethod\n def now():\n t = localtime()\n tm_sec = t.tm_sec\n if tm_sec == 60:\n tm_sec = 59\n return datetime(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, tm_sec)\n\n def __str__(self):\n return f\"{self.year}-{self.month:02}-{self.day:02} {self.hour:02}:{self.minute:02}:{self.second:02}\"\n\n def __repr__(self):\n return f\"datetime.datetime({self.year}, {self.month}, {self.day}, {self.hour}, {self.minute}, {self.second})\"\n\n def __cmp(self, other, op):\n if not isinstance(other, datetime):\n return NotImplemented\n if self.year != other.year:\n return op(self.year, other.year)\n if self.month != other.month:\n return op(self.month, other.month)\n if self.day != other.day:\n return op(self.day, other.day)\n if self.hour != other.hour:\n return op(self.hour, other.hour)\n if self.minute != other.minute:\n return op(self.minute, other.minute)\n return op(self.second, other.second)\n\n def __eq__(self, other) -> bool:\n return self.__cmp(other, operator.eq)\n \n def __ne__(self, other) -> bool:\n return self.__cmp(other, operator.ne)\n \n def __lt__(self, other) -> bool:\n return self.__cmp(other, operator.lt)\n \n def __le__(self, other) -> bool:\n return self.__cmp(other, operator.le)\n \n def __gt__(self, other) -> bool:\n return self.__cmp(other, operator.gt)\n \n def __ge__(self, other) -> bool:\n return self.__cmp(other, operator.ge)\n\n\n";
|
||||
const char kPythonLibs_functools[] = "class cache:\n def __init__(self, f):\n self.f = f\n self.cache = {}\n\n def __call__(self, *args):\n if args not in self.cache:\n self.cache[args] = self.f(*args)\n return self.cache[args]\n \ndef reduce(function, sequence, initial=...):\n it = iter(sequence)\n if initial is ...:\n try:\n value = next(it)\n except StopIteration:\n raise TypeError(\"reduce() of empty sequence with no initial value\")\n else:\n value = initial\n for element in it:\n value = function(value, element)\n return value\n\nclass partial:\n def __init__(self, f, *args, **kwargs):\n self.f = f\n if not callable(f):\n raise TypeError(\"the first argument must be callable\")\n self.args = args\n self.kwargs = kwargs\n\n def __call__(self, *args, **kwargs):\n kwargs.update(self.kwargs)\n return self.f(*self.args, *args, **kwargs)\n\n";
|
||||
const char kPythonLibs_functools[] = "class cache:\n def __init__(self, f):\n self.f = f\n self.cache = {}\n\n def __call__(self, *args):\n if args not in self.cache:\n self.cache[args] = self.f(*args)\n return self.cache[args]\n \nclass lru_cache:\n def __init__(self, maxsize=128):\n self.maxsize = maxsize\n self.cache = {}\n\n def __call__(self, f):\n def wrapped(*args):\n if args in self.cache:\n res = self.cache.pop(args)\n self.cache[args] = res\n return res\n \n res = f(*args)\n if len(self.cache) >= self.maxsize:\n first_key = next(iter(self.cache))\n self.cache.pop(first_key)\n self.cache[args] = res\n return res\n return wrapped\n \ndef reduce(function, sequence, initial=...):\n it = iter(sequence)\n if initial is ...:\n try:\n value = next(it)\n except StopIteration:\n raise TypeError(\"reduce() of empty sequence with no initial value\")\n else:\n value = initial\n for element in it:\n value = function(value, element)\n return value\n\nclass partial:\n def __init__(self, f, *args, **kwargs):\n self.f = f\n if not callable(f):\n raise TypeError(\"the first argument must be callable\")\n self.args = args\n self.kwargs = kwargs\n\n def __call__(self, *args, **kwargs):\n kwargs.update(self.kwargs)\n return self.f(*self.args, *args, **kwargs)\n\n";
|
||||
const char kPythonLibs_heapq[] = "# Heap queue algorithm (a.k.a. priority queue)\ndef heappush(heap, item):\n \"\"\"Push item onto heap, maintaining the heap invariant.\"\"\"\n heap.append(item)\n _siftdown(heap, 0, len(heap)-1)\n\ndef heappop(heap):\n \"\"\"Pop the smallest item off the heap, maintaining the heap invariant.\"\"\"\n lastelt = heap.pop() # raises appropriate IndexError if heap is empty\n if heap:\n returnitem = heap[0]\n heap[0] = lastelt\n _siftup(heap, 0)\n return returnitem\n return lastelt\n\ndef heapreplace(heap, item):\n \"\"\"Pop and return the current smallest value, and add the new item.\n\n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed-size heap. Note that the value\n returned may be larger than item! That constrains reasonable uses of\n this routine unless written as part of a conditional replacement:\n\n if item > heap[0]:\n item = heapreplace(heap, item)\n \"\"\"\n returnitem = heap[0] # raises appropriate IndexError if heap is empty\n heap[0] = item\n _siftup(heap, 0)\n return returnitem\n\ndef heappushpop(heap, item):\n \"\"\"Fast version of a heappush followed by a heappop.\"\"\"\n if heap and heap[0] < item:\n item, heap[0] = heap[0], item\n _siftup(heap, 0)\n return item\n\ndef heapify(x):\n \"\"\"Transform list into a heap, in-place, in O(len(x)) time.\"\"\"\n n = len(x)\n # Transform bottom-up. The largest index there's any point to looking at\n # is the largest with a child index in-range, so must have 2*i + 1 < n,\n # or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n # j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is\n # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n for i in reversed(range(n//2)):\n _siftup(x, i)\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos\n# is the index of a leaf with a possibly out-of-order value. Restore the\n# heap invariant.\ndef _siftdown(heap, startpos, pos):\n newitem = heap[pos]\n # Follow the path to the root, moving parents down until finding a place\n # newitem fits.\n while pos > startpos:\n parentpos = (pos - 1) >> 1\n parent = heap[parentpos]\n if newitem < parent:\n heap[pos] = parent\n pos = parentpos\n continue\n break\n heap[pos] = newitem\n\ndef _siftup(heap, pos):\n endpos = len(heap)\n startpos = pos\n newitem = heap[pos]\n # Bubble up the smaller child until hitting a leaf.\n childpos = 2*pos + 1 # leftmost child position\n while childpos < endpos:\n # Set childpos to index of smaller child.\n rightpos = childpos + 1\n if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n childpos = rightpos\n # Move the smaller child up.\n heap[pos] = heap[childpos]\n pos = childpos\n childpos = 2*pos + 1\n # The leaf at pos is empty now. Put newitem there, and bubble it up\n # to its final resting place (by sifting its parents down).\n heap[pos] = newitem\n _siftdown(heap, startpos, pos)";
|
||||
const char kPythonLibs_linalg[] = "from vmath import *";
|
||||
const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operator.html#mapping-operators-to-functions\n\ndef le(a, b): return a <= b\ndef lt(a, b): return a < b\ndef ge(a, b): return a >= b\ndef gt(a, b): return a > b\ndef eq(a, b): return a == b\ndef ne(a, b): return a != b\n\ndef and_(a, b): return a & b\ndef or_(a, b): return a | b\ndef xor(a, b): return a ^ b\ndef invert(a): return ~a\ndef lshift(a, b): return a << b\ndef rshift(a, b): return a >> b\n\ndef is_(a, b): return a is b\ndef is_not(a, b): return a is not b\ndef not_(a): return not a\ndef truth(a): return bool(a)\ndef contains(a, b): return b in a\n\ndef add(a, b): return a + b\ndef sub(a, b): return a - b\ndef mul(a, b): return a * b\ndef truediv(a, b): return a / b\ndef floordiv(a, b): return a // b\ndef mod(a, b): return a % b\ndef pow(a, b): return a ** b\ndef neg(a): return -a\ndef matmul(a, b): return a @ b\n\ndef getitem(a, b): return a[b]\ndef setitem(a, b, c): a[b] = c\ndef delitem(a, b): del a[b]\n\ndef iadd(a, b): a += b; return a\ndef isub(a, b): a -= b; return a\ndef imul(a, b): a *= b; return a\ndef itruediv(a, b): a /= b; return a\ndef ifloordiv(a, b): a //= b; return a\ndef imod(a, b): a %= b; return a\n# def ipow(a, b): a **= b; return a\n# def imatmul(a, b): a @= b; return a\ndef iand(a, b): a &= b; return a\ndef ior(a, b): a |= b; return a\ndef ixor(a, b): a ^= b; return a\ndef ilshift(a, b): a <<= b; return a\ndef irshift(a, b): a >>= b; return a\n";
|
||||
|
@ -132,7 +132,7 @@ void VM__ctor(VM* self) {
|
||||
validate(tp_code, pk_code__register());
|
||||
|
||||
validate(tp_dict, pk_dict__register());
|
||||
validate(tp_dict_items, pk_dict_items__register());
|
||||
validate(tp_dict_iterator, pk_dict_items__register());
|
||||
|
||||
validate(tp_property, pk_property__register());
|
||||
validate(tp_star_wrapper, pk_newtype("star_wrapper", tp_object, NULL, NULL, false, true));
|
||||
|
@ -56,6 +56,7 @@ static uint32_t Dict__next_cap(uint32_t cap) {
|
||||
typedef struct {
|
||||
DictEntry* curr;
|
||||
DictEntry* end;
|
||||
int mode; // 0: keys, 1: values, 2: items
|
||||
} DictIterator;
|
||||
|
||||
static void Dict__ctor(Dict* self, uint32_t capacity, int entries_capacity) {
|
||||
@ -241,9 +242,10 @@ static int Dict__pop(Dict* self, py_Ref key) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DictIterator__ctor(DictIterator* self, Dict* dict) {
|
||||
static void DictIterator__ctor(DictIterator* self, Dict* dict, int mode) {
|
||||
self->curr = dict->entries.data;
|
||||
self->end = self->curr + dict->entries.length;
|
||||
self->mode = mode;
|
||||
}
|
||||
|
||||
static DictEntry* DictIterator__next(DictIterator* self) {
|
||||
@ -377,7 +379,7 @@ static bool dict__eq__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
DictIterator iter;
|
||||
DictIterator__ctor(&iter, self);
|
||||
DictIterator__ctor(&iter, self, 2);
|
||||
// for each self key
|
||||
while(1) {
|
||||
DictEntry* entry = DictIterator__next(&iter);
|
||||
@ -463,48 +465,34 @@ static bool dict_pop(int argc, py_Ref argv) {
|
||||
py_Ref default_val = argc == 3 ? py_arg(2) : py_None();
|
||||
int res = Dict__pop(self, py_arg(1));
|
||||
if(res == -1) return false;
|
||||
if(res == 0) { py_assign(py_retval(), default_val); }
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dict_items(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Dict* self = py_touserdata(argv);
|
||||
DictIterator* ud = py_newobject(py_retval(), tp_dict_items, 1, sizeof(DictIterator));
|
||||
DictIterator__ctor(ud, self);
|
||||
py_setslot(py_retval(), 0, argv); // keep a reference to the dict
|
||||
if(res == 0) py_assign(py_retval(), default_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dict_keys(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Dict* self = py_touserdata(argv);
|
||||
py_Ref p = py_newtuple(py_retval(), self->length);
|
||||
DictIterator iter;
|
||||
DictIterator__ctor(&iter, self);
|
||||
int i = 0;
|
||||
while(1) {
|
||||
DictEntry* entry = DictIterator__next(&iter);
|
||||
if(!entry) break;
|
||||
p[i++] = entry->key;
|
||||
}
|
||||
assert(i == self->length);
|
||||
DictIterator* ud = py_newobject(py_retval(), tp_dict_iterator, 1, sizeof(DictIterator));
|
||||
DictIterator__ctor(ud, self, 0);
|
||||
py_setslot(py_retval(), 0, argv); // keep a reference to the dict
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dict_values(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Dict* self = py_touserdata(argv);
|
||||
py_Ref p = py_newtuple(py_retval(), self->length);
|
||||
DictIterator iter;
|
||||
DictIterator__ctor(&iter, self);
|
||||
int i = 0;
|
||||
while(1) {
|
||||
DictEntry* entry = DictIterator__next(&iter);
|
||||
if(!entry) break;
|
||||
p[i++] = entry->val;
|
||||
}
|
||||
assert(i == self->length);
|
||||
DictIterator* ud = py_newobject(py_retval(), tp_dict_iterator, 1, sizeof(DictIterator));
|
||||
DictIterator__ctor(ud, self, 1);
|
||||
py_setslot(py_retval(), 0, argv); // keep a reference to the dict
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dict_items(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Dict* self = py_touserdata(argv);
|
||||
DictIterator* ud = py_newobject(py_retval(), tp_dict_iterator, 1, sizeof(DictIterator));
|
||||
DictIterator__ctor(ud, self, 2);
|
||||
py_setslot(py_retval(), 0, argv); // keep a reference to the dict
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -521,15 +509,16 @@ py_Type pk_dict__register() {
|
||||
py_bindmagic(type, __repr__, dict__repr__);
|
||||
py_bindmagic(type, __eq__, dict__eq__);
|
||||
py_bindmagic(type, __ne__, dict__ne__);
|
||||
py_bindmagic(type, __iter__, dict_keys);
|
||||
|
||||
py_bindmethod(type, "clear", dict_clear);
|
||||
py_bindmethod(type, "copy", dict_copy);
|
||||
py_bindmethod(type, "update", dict_update);
|
||||
py_bindmethod(type, "get", dict_get);
|
||||
py_bindmethod(type, "pop", dict_pop);
|
||||
py_bindmethod(type, "items", dict_items);
|
||||
py_bindmethod(type, "keys", dict_keys);
|
||||
py_bindmethod(type, "values", dict_values);
|
||||
py_bindmethod(type, "items", dict_items);
|
||||
|
||||
py_setdict(py_tpobject(type), __hash__, py_None());
|
||||
return type;
|
||||
@ -541,16 +530,28 @@ static bool dict_items__next__(int argc, py_Ref argv) {
|
||||
DictIterator* iter = py_touserdata(py_arg(0));
|
||||
DictEntry* entry = (DictIterator__next(iter));
|
||||
if(entry) {
|
||||
py_Ref p = py_newtuple(py_retval(), 2);
|
||||
p[0] = entry->key;
|
||||
p[1] = entry->val;
|
||||
return true;
|
||||
switch(iter->mode) {
|
||||
case 0: // keys
|
||||
py_assign(py_retval(), &entry->key);
|
||||
return true;
|
||||
case 1: // values
|
||||
py_assign(py_retval(), &entry->val);
|
||||
return true;
|
||||
case 2: // items
|
||||
{
|
||||
py_Ref p = py_newtuple(py_retval(), 2);
|
||||
p[0] = entry->key;
|
||||
p[1] = entry->val;
|
||||
return true;
|
||||
}
|
||||
default: c11__unreachable();
|
||||
}
|
||||
}
|
||||
return StopIteration();
|
||||
}
|
||||
|
||||
py_Type pk_dict_items__register() {
|
||||
py_Type type = pk_newtype("dict_items", tp_object, NULL, NULL, false, true);
|
||||
py_Type type = pk_newtype("dict_iterator", tp_object, NULL, NULL, false, true);
|
||||
py_bindmagic(type, __iter__, pk_wrapper__self);
|
||||
py_bindmagic(type, __next__, dict_items__next__);
|
||||
return type;
|
||||
|
@ -32,8 +32,8 @@ assert tinydict == updated_dict
|
||||
|
||||
dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
|
||||
# dict is now ordered
|
||||
assert dishes.keys() == ('eggs', 'sausage', 'bacon', 'spam')
|
||||
assert dishes.values() == (2, 1, 1, 500)
|
||||
assert list(dishes.keys()) == ['eggs', 'sausage', 'bacon', 'spam']
|
||||
assert list(dishes.values()) == [2, 1, 1, 500]
|
||||
|
||||
d={1:"a",2:"b",3:"c"}
|
||||
result=[]
|
||||
@ -44,8 +44,8 @@ assert len(result) == 6
|
||||
|
||||
del d[2]
|
||||
assert len(d) == 2
|
||||
assert d.keys() == (1, 3)
|
||||
assert d.values() == ('a', 'c')
|
||||
assert list(d.keys()) == [1, 3]
|
||||
assert list(d.values()) == ['a', 'c']
|
||||
del d[1]
|
||||
del d[3]
|
||||
assert len(d) == 0
|
||||
@ -77,11 +77,11 @@ a = {'g': 0}
|
||||
|
||||
a['ball_3'] = 0
|
||||
a['ball_4'] = 0
|
||||
assert a.keys() == ('g', 'ball_3', 'ball_4')
|
||||
assert list(a.keys()) == ['g', 'ball_3', 'ball_4']
|
||||
del a['ball_3']
|
||||
assert a.keys() == ('g', 'ball_4')
|
||||
assert list(a.keys()) == ['g', 'ball_4']
|
||||
del a['ball_4']
|
||||
assert a.keys() == ('g',)
|
||||
assert list(a.keys()) == ['g',]
|
||||
del a['g']
|
||||
assert len(a) == 0
|
||||
|
||||
@ -147,4 +147,11 @@ for i in range(-1000, 1000):
|
||||
e = {}
|
||||
for i in range(-10000, 10000, 3):
|
||||
e[i] = i
|
||||
assert e[i] == i
|
||||
assert e[i] == i
|
||||
|
||||
# test iter
|
||||
d = {'1': 1, 222: 2, '333': 3}
|
||||
assert list(d) == ['1', 222, '333']
|
||||
assert list(d.keys()) == ['1', 222, '333']
|
||||
assert list(d.values()) == [1, 2, 3]
|
||||
assert list(d.items()) == [('1', 1), (222, 2), ('333', 3)]
|
||||
|
@ -23,3 +23,34 @@ sub_10 = partial(sub, b=10)
|
||||
assert sub_10(20) == 10
|
||||
assert sub_10(30) == 20
|
||||
|
||||
# test lru_cache
|
||||
from functools import lru_cache
|
||||
miss_keys = []
|
||||
|
||||
@lru_cache(maxsize=3)
|
||||
def test_f(x: int):
|
||||
miss_keys.append(x)
|
||||
return x
|
||||
|
||||
assert test_f(1) == 1 and miss_keys == [1]
|
||||
# [1]
|
||||
assert test_f(2) == 2 and miss_keys == [1, 2]
|
||||
# [1, 2]
|
||||
assert test_f(3) == 3 and miss_keys == [1, 2, 3]
|
||||
# [1, 2, 3]
|
||||
assert test_f(1) == 1 and miss_keys == [1, 2, 3]
|
||||
# [2, 3, 1]
|
||||
assert test_f(2) == 2 and miss_keys == [1, 2, 3]
|
||||
# [3, 1, 2]
|
||||
assert test_f(4) == 4 and miss_keys == [1, 2, 3, 4]
|
||||
# [1, 2, 4]
|
||||
assert test_f(3) == 3 and miss_keys == [1, 2, 3, 4, 3]
|
||||
# [2, 4, 3]
|
||||
assert test_f(2) == 2 and miss_keys == [1, 2, 3, 4, 3]
|
||||
# [4, 3, 2]
|
||||
assert test_f(5) == 5 and miss_keys == [1, 2, 3, 4, 3, 5]
|
||||
# [3, 2, 5]
|
||||
assert test_f(3) == 3 and miss_keys == [1, 2, 3, 4, 3, 5]
|
||||
# [2, 5, 3]
|
||||
assert test_f(1) == 1 and miss_keys == [1, 2, 3, 4, 3, 5, 1]
|
||||
# [5, 3, 1]
|
||||
|
Loading…
x
Reference in New Issue
Block a user