diff --git a/CHANGES/712.feature.rst b/CHANGES/712.feature.rst new file mode 100644 index 00000000..ee4d2b67 --- /dev/null +++ b/CHANGES/712.feature.rst @@ -0,0 +1,2 @@ +Refractor methods `__len__`, `__iadd__`, `index`, `extend`, `append`, `count` and `__deecopy__` and add notes for `pop`, `append` and `extend` for contributors who are looking to +refractor anything further -- by :user:`Vizonex`. diff --git a/frozenlist/_frozenlist.pyx b/frozenlist/_frozenlist.pyx index a82d8c8f..a1921a1f 100644 --- a/frozenlist/_frozenlist.pyx +++ b/frozenlist/_frozenlist.pyx @@ -2,6 +2,12 @@ # distutils: language = c++ from cpython.bool cimport PyBool_FromLong +from cpython.list cimport PyList_GET_SIZE +from cpython.long cimport PyLong_FromLong +from cpython.sequence cimport ( + PySequence_Count, + PySequence_InPlaceConcat, +) from libcpp.atomic cimport atomic import copy @@ -31,9 +37,6 @@ cdef class FrozenList: if self._frozen.load(): raise RuntimeError("Cannot modify frozen list.") - cdef inline object _fast_len(self): - return len(self._items) - def freeze(self): self._frozen.store(True) @@ -49,7 +52,8 @@ cdef class FrozenList: del self._items[index] def __len__(self): - return self._fast_len() + # Cython does less expensive calling if PyList_GET_SIZE is utilized + return PyList_GET_SIZE(self._items) def __iter__(self): return self._items.__iter__() @@ -71,20 +75,20 @@ cdef class FrozenList: if op == 5: # => return list(self) >= other - def insert(self, pos, item): + def insert(self, *args): self._check_frozen() - self._items.insert(pos, item) + self._items.insert(*args) def __contains__(self, item): return item in self._items def __iadd__(self, items): self._check_frozen() - self._items += list(items) + self._items = PySequence_InPlaceConcat(self._items, items) return self - def index(self, item): - return self._items.index(item) + def index(self, *args): + return self._items.index(*args) def remove(self, item): self._check_frozen() @@ -96,22 +100,29 @@ cdef class FrozenList: def extend(self, items): self._check_frozen() - self._items += list(items) + # Cython will generate __Pyx_PyList_Extend + self._items.extend(items) def reverse(self): self._check_frozen() + # Cython will do PyList_Reverse by default... self._items.reverse() def pop(self, index=-1): + # XXX: Current pop is impossible to refactor and may + # require the Cython maintainers to brainstorm a new idea. self._check_frozen() return self._items.pop(index) def append(self, item): self._check_frozen() - return self._items.append(item) + # Cython will generate an appropriate function for append + self._items.append(item) def count(self, item): - return self._items.count(item) + # NOTE: doing self._items.count(item) Generates expensive call + # As for PyLong_FromLong it's a bit faster to call the direct C-API + return PyLong_FromLong(PySequence_Count(self._items, item)) def __repr__(self): return ''.format(self._frozen.load(), @@ -140,7 +151,8 @@ cdef class FrozenList: # Preserve frozen state if self._frozen.load(): - new_list.freeze() + # faster to call .store directly rather than freeze() + new_list._frozen.store(True) return new_list