hashlist 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hashlist-0.1.0/.gitignore +10 -0
- hashlist-0.1.0/.python-version +1 -0
- hashlist-0.1.0/CMakeLists.txt +11 -0
- hashlist-0.1.0/PKG-INFO +186 -0
- hashlist-0.1.0/README.md +178 -0
- hashlist-0.1.0/hashlist/__init__.py +136 -0
- hashlist-0.1.0/pyproject.toml +19 -0
- hashlist-0.1.0/src/main.cpp +343 -0
- hashlist-0.1.0/test/test_hashlist.py +238 -0
- hashlist-0.1.0/test_run.py +140 -0
- hashlist-0.1.0/uv.lock +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
project(hashlist LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
|
|
7
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
8
|
+
|
|
9
|
+
pybind11_add_module(hashlist_backend src/main.cpp)
|
|
10
|
+
|
|
11
|
+
install(TARGETS hashlist_backend DESTINATION hashlist)
|
hashlist-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: hashlist
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A high-performance hybrid list with O(1) hashing speed for Python.
|
|
5
|
+
Author-Email: Fort66 <mamonov.1966@yandex.ru>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
*Read this in other languages: [Русский](README_RU.md).*
|
|
10
|
+
# hashlist
|
|
11
|
+
|
|
12
|
+
A high-performance, drop-in replacement for the standard Python `list` with **O(1) search and index speed** powered by a native C++17 backend via `pybind11`.
|
|
13
|
+
|
|
14
|
+
## 🚀 The Problem & Our Solution
|
|
15
|
+
|
|
16
|
+
In Python, checking if an item exists in a standard `list` (`item in my_list`) or finding its position (`my_list.index(item)`) takes **O(N) linear time**. As your dataset grows to millions of elements, these operations become a massive bottleneck for the CPU.
|
|
17
|
+
|
|
18
|
+
`hashlist` solves this by introducing a highly optimized hybrid data structure. It combines a fast native C++ `std::vector` (preserving original insertion order, indices, and slices) with an advanced C++ `std::unordered_map<ssize_t, std::vector<size_t>>` lookup bucket map on the machine-instruction level.
|
|
19
|
+
|
|
20
|
+
The result? Lookups, counter extractions, and index resolutions run in **O(1) constant time**, making it over **6,100x faster** than a standard Python list on heavy heterogeneous workloads.
|
|
21
|
+
|
|
22
|
+
## ✨ Features
|
|
23
|
+
|
|
24
|
+
- **⚡ Blazing Fast Lookups:** Constant time O(1) searches regardless of the list size.
|
|
25
|
+
- **🚀 Ultra-Fast Index Resolution:** Unlike standard lists, finding the position of an element takes constant O(1) time due to low-level continuous index bucket tracking.
|
|
26
|
+
- **🧠 100% Type Agnostic & Safe:** Safely handles standard hashable types (ints, strings) as well as mutable unhashable structures (nested `list`, `dict`, `set`) through a deterministic C++ recursive freeze algorithm.
|
|
27
|
+
- **📦 Drop-in Replacement:** Fully implements the CPython `MutableSequence` protocol. Supports extended slices (`hl[0:5:2]`), negative indexing, mathematical operators (`+`, `*`, `+=`, `*=`), and global functions like `sorted()` and `reversed()`.
|
|
28
|
+
- **🪶 Memory Optimized:** Built using `__slots__` at the wrapper level. An empty `hashlist` takes only **40 bytes** of memory due to lazy structures initialization.
|
|
29
|
+
|
|
30
|
+
## 📊 Performance Benchmark
|
|
31
|
+
|
|
32
|
+
Searching for a nested mutable dictionary at the very end of a **5,000,000** element heterogeneous collection:
|
|
33
|
+
|
|
34
|
+
| Container Type | Search Time (Seconds) | Algorithmic Complexity | Performance Boost |
|
|
35
|
+
| :--- | :--- | :--- | :--- |
|
|
36
|
+
| Standard Python `list` | ~0.14096335 s | O(N) (Linear scan) | Baseline |
|
|
37
|
+
| **`hashlist` (C++ Backend)** | **0.00002302 s** | **O(1) (Constant map)** | **6,123x Faster** |
|
|
38
|
+
|
|
39
|
+
*Benchmark executed under Python 3.13.15 on CachyOS (Arch Linux, GCC 16.2.1).*
|
|
40
|
+
|
|
41
|
+
## 🛠️ Complete Methods Specification
|
|
42
|
+
|
|
43
|
+
`hashlist` fully supports all 12 canonical Python list mutation and lookup methods with unique C++ architectural optimizations:
|
|
44
|
+
|
|
45
|
+
| Method | Description | Complexity | C++ Advantage |
|
|
46
|
+
| :--- | :--- | :--- | :--- |
|
|
47
|
+
| `hl.append(x)` | Appends element `x` to the end. Supports nested structures. | **O(1)** amortized | Native C++ memory allocation efficiency |
|
|
48
|
+
| `hl.extend(iterable)` | Appends all elements from the iterable to the end. | **O(M)** | Fast sequential batch insertion |
|
|
49
|
+
| `hl.insert(i, x)` | Inserts element `x` at position `i` shifting vectors. | O(N-i) native shift | Direct block memory shift on native CPU instructions level |
|
|
50
|
+
| `hl.remove(x)` | Removes the first occurrence of value `x`. | **O(1) lookup check** + O(N) shift | **Instant failure** if item is missing (completely avoids linear vector scans) |
|
|
51
|
+
| `hl.clear()` | Removes all elements, completely resetting container. | **O(1)** | Instant bucket and vector memory deallocation |
|
|
52
|
+
| `hl.index(x)` | Returns the integer index of the first occurrence of `x`. | **🚀 O(1) constant** | **Instant index resolution** directly from the C++ bucket map without linear scans |
|
|
53
|
+
| `hl.pop(i=-1)` | Removes and returns the element at index `i`. | **🔥 O(1) for end pop** / O(N-i) shift | Activated **Fast-Path optimization** during end pop operations |
|
|
54
|
+
| `hl.count(x)` | Returns the number of times `x` appears in the list. | **🚀 O(1) constant** | Blazing fast value counter extracted directly from hash-map |
|
|
55
|
+
| `hl.sort()` | Sorts the items on place. Supports `key` and `reverse`. | \(O(N \log N)\) | Fast Timsort pass directly over continuous native storage |
|
|
56
|
+
| `hl.reverse()` | Reverses the elements of the list on place. | **O(N)** | Highly optimized `std::reverse` pointer swapping |
|
|
57
|
+
| `hl.copy()` | Returns a shallow copy of the container. | **O(N)** | Fast iteration and element mirroring |
|
|
58
|
+
|
|
59
|
+
## 💻 Quick Start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from hashlist import hl, hashlist
|
|
63
|
+
|
|
64
|
+
# 1. Quick creation using the factory function
|
|
65
|
+
my_list = hl(10, 20, 30, "apple")
|
|
66
|
+
|
|
67
|
+
# 2. Add complex nested unhashable structures safely
|
|
68
|
+
my_list.append({"name": "Ivan", "id": 66})
|
|
69
|
+
|
|
70
|
+
# 3. Instant O(1) searches and index resolution on C++ level
|
|
71
|
+
print("apple" in my_list) # True
|
|
72
|
+
print(my_list.index("apple")) # 3 (Instant O(1) extraction!)
|
|
73
|
+
print({"id": 66, "name": "Ivan"} in my_list) # True (Order independent!)
|
|
74
|
+
|
|
75
|
+
# 4. Standard Python functions and slices work seamlessly
|
|
76
|
+
for item in reversed(my_list):
|
|
77
|
+
print(item)
|
|
78
|
+
|
|
79
|
+
sub_slice = my_list[1:4]
|
|
80
|
+
print(20 in sub_slice) # True
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 🏗️ Development & Testing
|
|
84
|
+
|
|
85
|
+
The package uses `scikit-build-core` and `pybind11` for seamless native compilation inside `uv` environments.
|
|
86
|
+
|
|
87
|
+
To run the complete automated test suite (including performance, memory, and stress testing):
|
|
88
|
+
```bash
|
|
89
|
+
uv run pytest -v -s
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 📄 License
|
|
93
|
+
|
|
94
|
+
Distributed under the MIT License. Developed by Fort66 (mamonov.1966@yandex.ru).
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# hashlist
|
|
103
|
+
|
|
104
|
+
Высокопроизводительная замена стандартного Python `list` с **O(1) скоростью поиска и разрешения индексов**, работающая на нативном C++17 бэкенде через `pybind11`.
|
|
105
|
+
|
|
106
|
+
## 🚀 Проблема и наше решение
|
|
107
|
+
|
|
108
|
+
В Python проверка наличия элемента в стандартном списке (`item in my_list`) или поиск его позиции (`my_list.index(item)`) занимает **O(N) линейное время**. По мере того как ваш набор данных растет до миллионов элементов, эти операции превращаются в критическое «узкое место» для процессора.
|
|
109
|
+
|
|
110
|
+
`hashlist` решает эту проблему за счет внедрения оптимизированной гибридной структуры данных. Она сочетает в себе быстрый нативный C++ `std::vector` (сохраняющий исходный порядок вставки, индексы и срезы) с продвинутой хэш-картой `std::unordered_map<ssize_t, std::vector<size_t>>` на уровне машинных инструкций.
|
|
111
|
+
|
|
112
|
+
Результат? Поиск, подсчет вхождений и извлечение индексов выполняются за **константное время O(1)**, что делает контейнер более чем в **6100 раз быстрее** стандартного Python-списка на тяжелых разнородных нагрузках.
|
|
113
|
+
|
|
114
|
+
## ✨ Особенности
|
|
115
|
+
|
|
116
|
+
- **⚡ Молниеносный поиск:** Константное время O(1) для оператора `in` независимо от размера списка.
|
|
117
|
+
- **🚀 Моментальное разрешение индексов:** В отличие от стандартных списков, метод `.index()` находит позицию элемента за O(1) благодаря непрерывному низкоуровневому отслеживанию бакетов.
|
|
118
|
+
- **🧠 100% всеядность и безопасность типов:** Надежно обрабатывает как стандартные хэшируемые типы (числа, строки), так и изменяемые нехэшируемые структуры (вложенные `list`, `dict`, `set`) с помощью детерминированного алгоритма рекурсивной «заморозки» на C++.
|
|
119
|
+
- **📦 Полноценная drop-in замена:** Полностью реализует протокол `MutableSequence` в CPython. Из коробки поддерживаются сложные срезы (`hl[0:5:2]`), отрицательная индексация, математические операторы (`+`, `*`, `+=`, `*=`) и глобальные встроенные функции `sorted()` и `reversed()`.
|
|
120
|
+
- **🪶 Оптимизация памяти:** Построена с использованием `__slots__` на уровне Python-оболочки. Пустой `hashlist` занимает всего **40 байт** памяти благодаря ленивой инициализации внутренних C++ структур.
|
|
121
|
+
|
|
122
|
+
## 📊 Бенчмарк производительности
|
|
123
|
+
|
|
124
|
+
Поиск сложного вложенного изменяемого словаря в самом конце перемешанной коллекции из **5 000 000** разнородных элементов:
|
|
125
|
+
|
|
126
|
+
| Тип контейнера | Время поиска (Секунды) | Алгоритмическая сложность | Прирост скорости |
|
|
127
|
+
| :--- | :--- | :--- | :--- |
|
|
128
|
+
| Стандартный Python `list` | ~0.14096335 сек | O(N) (Линейный проход) | Базовый уровень |
|
|
129
|
+
| **`hashlist` (C++ ядро)** | **0.00002302 сек** | **O(1) (Константная карта)** | **В 6123.5 раза быстрее** |
|
|
130
|
+
|
|
131
|
+
*Тестирование проводилось под Python 3.13.15 на операционной системе CachyOS (Arch Linux, компилятор GCC 16.2.1).*
|
|
132
|
+
|
|
133
|
+
## 🛠️ Спецификация поддерживаемых методов
|
|
134
|
+
|
|
135
|
+
`hashlist` полностью поддерживает все 12 канонических методов мутации и поиска стандартного списка со специальными архитектурными оптимизациями C++:
|
|
136
|
+
|
|
137
|
+
| Метод | Описание | Сложность | Преимущество C++ бэкенда |
|
|
138
|
+
| :--- | :--- | :--- | :--- |
|
|
139
|
+
| `hl.append(x)` | Добавляет элемент `x` в конец. Поддерживает вложенные структуры. | **O(1)** амортизировано | Эффективность нативного выделения памяти C++ |
|
|
140
|
+
| `hl.extend(iterable)` | Добавляет в конец все элементы из переданной последовательности. | **O(M)** | Быстрая последовательная пакетная вставка (зависит только от размера принесенных данных) |
|
|
141
|
+
| `hl.insert(i, x)` | Вставляет элемент `x` на позицию `i` со сдвигом вектора. | O(N-i) нативный сдвиг | Прямой сдвиг блока памяти на уровне машинных инструкций процессора |
|
|
142
|
+
| `hl.remove(x)` | Удаляет первое вхождение значения `x`. | **O(1) проверка поиска** + O(N) сдвиг | **Мгновенный вылет с ошибкой** за O(1), если элемента нет (без линейного сканирования вектора) |
|
|
143
|
+
| `hl.clear()` | Удаляет все элементы, полностью освобождая память. | **O(1)** | Моментальная очистка бакетов хэш-карты и деаллокация вектора |
|
|
144
|
+
| `hl.index(x)` | Возвращает целочисленный индекс первого вхождения `x`. | **🚀 O(1) константа** | **Мгновенное разрешение индекса** напрямую из карты бакетов без линейных переборов |
|
|
145
|
+
| `hl.pop(i=-1)` | Удаляет и возвращает элемент по индексу `i`. | **🔥 O(1) для извлечения с конца** / O(N-i) сдвиг | Активация оптимизации **Fast-Path** при дефолтном удалении последнего элемента |
|
|
146
|
+
| `hl.count(x)` | Возвращает количество повторений элемента `x` в списке. | **🚀 O(1) константа** | Молниеносное извлечение размера вектора индексов напрямую из хэш-карты |
|
|
147
|
+
| `hl.sort()` | Сортирует элементы на месте. Поддерживает `key` и `reverse`. | \(O(N \log N)\) | Быстрый проход алгоритма Timsort напрямую по непрерывному C-массиву |
|
|
148
|
+
| `hl.reverse()` | Разворачивает элементы списка на месте. | **O(N)** | Высокооптимизированная перестановка указателей через `std::reverse` |
|
|
149
|
+
| `hl.copy()` | Создает поверхностную копию контейнера. | **O(N)** | Быстрая нативная итерация и зеркалирование элементов |
|
|
150
|
+
|
|
151
|
+
## 💻 Быстрый старт
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from hashlist import hl, hashlist
|
|
155
|
+
|
|
156
|
+
# 1. Быстрое создание через фабричную функцию
|
|
157
|
+
my_list = hl(10, 20, 30, "яблоко")
|
|
158
|
+
|
|
159
|
+
# 2. Безопасное добавление сложных нехэшируемых структур
|
|
160
|
+
my_list.append({"имя": "Иван", "id": 66})
|
|
161
|
+
|
|
162
|
+
# 3. Моментальный O(1) поиск и извлечение индексов на уровне C++
|
|
163
|
+
print("яблоко" in my_list) # True
|
|
164
|
+
print(my_list.index("яблоко")) # 3 (Мгновенно за O(1)!)
|
|
165
|
+
print({"id": 66, "имя": "Иван"} in my_list) # True (Независимо от порядка ключей!)
|
|
166
|
+
|
|
167
|
+
# 4. Стандартные функции Python и срезы работают бесшовно
|
|
168
|
+
for item in reversed(my_list):
|
|
169
|
+
print(item)
|
|
170
|
+
|
|
171
|
+
sub_slice = my_list[1:4]
|
|
172
|
+
print(20 in sub_slice) # True
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## 🏗️ Разработка и тестирование
|
|
176
|
+
|
|
177
|
+
Для автоматической компиляции и сборки расширения внутри изолированных окружений `uv` используются `scikit-build-core` и `pybind11`.
|
|
178
|
+
|
|
179
|
+
Чтобы запустить полный автоматизированный набор тестов (включая нагрузочное стресс-тестирование и бенчмарки скорости):
|
|
180
|
+
```bash
|
|
181
|
+
uv run pytest -v -s
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 📄 Лицензия
|
|
185
|
+
|
|
186
|
+
Распространяется под лицензией MIT. Разработчик: Fort66 (mamonov.1966@yandex.ru).
|
hashlist-0.1.0/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
*Read this in other languages: [Русский](README_RU.md).*
|
|
2
|
+
# hashlist
|
|
3
|
+
|
|
4
|
+
A high-performance, drop-in replacement for the standard Python `list` with **O(1) search and index speed** powered by a native C++17 backend via `pybind11`.
|
|
5
|
+
|
|
6
|
+
## 🚀 The Problem & Our Solution
|
|
7
|
+
|
|
8
|
+
In Python, checking if an item exists in a standard `list` (`item in my_list`) or finding its position (`my_list.index(item)`) takes **O(N) linear time**. As your dataset grows to millions of elements, these operations become a massive bottleneck for the CPU.
|
|
9
|
+
|
|
10
|
+
`hashlist` solves this by introducing a highly optimized hybrid data structure. It combines a fast native C++ `std::vector` (preserving original insertion order, indices, and slices) with an advanced C++ `std::unordered_map<ssize_t, std::vector<size_t>>` lookup bucket map on the machine-instruction level.
|
|
11
|
+
|
|
12
|
+
The result? Lookups, counter extractions, and index resolutions run in **O(1) constant time**, making it over **6,100x faster** than a standard Python list on heavy heterogeneous workloads.
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
- **⚡ Blazing Fast Lookups:** Constant time O(1) searches regardless of the list size.
|
|
17
|
+
- **🚀 Ultra-Fast Index Resolution:** Unlike standard lists, finding the position of an element takes constant O(1) time due to low-level continuous index bucket tracking.
|
|
18
|
+
- **🧠 100% Type Agnostic & Safe:** Safely handles standard hashable types (ints, strings) as well as mutable unhashable structures (nested `list`, `dict`, `set`) through a deterministic C++ recursive freeze algorithm.
|
|
19
|
+
- **📦 Drop-in Replacement:** Fully implements the CPython `MutableSequence` protocol. Supports extended slices (`hl[0:5:2]`), negative indexing, mathematical operators (`+`, `*`, `+=`, `*=`), and global functions like `sorted()` and `reversed()`.
|
|
20
|
+
- **🪶 Memory Optimized:** Built using `__slots__` at the wrapper level. An empty `hashlist` takes only **40 bytes** of memory due to lazy structures initialization.
|
|
21
|
+
|
|
22
|
+
## 📊 Performance Benchmark
|
|
23
|
+
|
|
24
|
+
Searching for a nested mutable dictionary at the very end of a **5,000,000** element heterogeneous collection:
|
|
25
|
+
|
|
26
|
+
| Container Type | Search Time (Seconds) | Algorithmic Complexity | Performance Boost |
|
|
27
|
+
| :--- | :--- | :--- | :--- |
|
|
28
|
+
| Standard Python `list` | ~0.14096335 s | O(N) (Linear scan) | Baseline |
|
|
29
|
+
| **`hashlist` (C++ Backend)** | **0.00002302 s** | **O(1) (Constant map)** | **6,123x Faster** |
|
|
30
|
+
|
|
31
|
+
*Benchmark executed under Python 3.13.15 on CachyOS (Arch Linux, GCC 16.2.1).*
|
|
32
|
+
|
|
33
|
+
## 🛠️ Complete Methods Specification
|
|
34
|
+
|
|
35
|
+
`hashlist` fully supports all 12 canonical Python list mutation and lookup methods with unique C++ architectural optimizations:
|
|
36
|
+
|
|
37
|
+
| Method | Description | Complexity | C++ Advantage |
|
|
38
|
+
| :--- | :--- | :--- | :--- |
|
|
39
|
+
| `hl.append(x)` | Appends element `x` to the end. Supports nested structures. | **O(1)** amortized | Native C++ memory allocation efficiency |
|
|
40
|
+
| `hl.extend(iterable)` | Appends all elements from the iterable to the end. | **O(M)** | Fast sequential batch insertion |
|
|
41
|
+
| `hl.insert(i, x)` | Inserts element `x` at position `i` shifting vectors. | O(N-i) native shift | Direct block memory shift on native CPU instructions level |
|
|
42
|
+
| `hl.remove(x)` | Removes the first occurrence of value `x`. | **O(1) lookup check** + O(N) shift | **Instant failure** if item is missing (completely avoids linear vector scans) |
|
|
43
|
+
| `hl.clear()` | Removes all elements, completely resetting container. | **O(1)** | Instant bucket and vector memory deallocation |
|
|
44
|
+
| `hl.index(x)` | Returns the integer index of the first occurrence of `x`. | **🚀 O(1) constant** | **Instant index resolution** directly from the C++ bucket map without linear scans |
|
|
45
|
+
| `hl.pop(i=-1)` | Removes and returns the element at index `i`. | **🔥 O(1) for end pop** / O(N-i) shift | Activated **Fast-Path optimization** during end pop operations |
|
|
46
|
+
| `hl.count(x)` | Returns the number of times `x` appears in the list. | **🚀 O(1) constant** | Blazing fast value counter extracted directly from hash-map |
|
|
47
|
+
| `hl.sort()` | Sorts the items on place. Supports `key` and `reverse`. | \(O(N \log N)\) | Fast Timsort pass directly over continuous native storage |
|
|
48
|
+
| `hl.reverse()` | Reverses the elements of the list on place. | **O(N)** | Highly optimized `std::reverse` pointer swapping |
|
|
49
|
+
| `hl.copy()` | Returns a shallow copy of the container. | **O(N)** | Fast iteration and element mirroring |
|
|
50
|
+
|
|
51
|
+
## 💻 Quick Start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from hashlist import hl, hashlist
|
|
55
|
+
|
|
56
|
+
# 1. Quick creation using the factory function
|
|
57
|
+
my_list = hl(10, 20, 30, "apple")
|
|
58
|
+
|
|
59
|
+
# 2. Add complex nested unhashable structures safely
|
|
60
|
+
my_list.append({"name": "Ivan", "id": 66})
|
|
61
|
+
|
|
62
|
+
# 3. Instant O(1) searches and index resolution on C++ level
|
|
63
|
+
print("apple" in my_list) # True
|
|
64
|
+
print(my_list.index("apple")) # 3 (Instant O(1) extraction!)
|
|
65
|
+
print({"id": 66, "name": "Ivan"} in my_list) # True (Order independent!)
|
|
66
|
+
|
|
67
|
+
# 4. Standard Python functions and slices work seamlessly
|
|
68
|
+
for item in reversed(my_list):
|
|
69
|
+
print(item)
|
|
70
|
+
|
|
71
|
+
sub_slice = my_list[1:4]
|
|
72
|
+
print(20 in sub_slice) # True
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 🏗️ Development & Testing
|
|
76
|
+
|
|
77
|
+
The package uses `scikit-build-core` and `pybind11` for seamless native compilation inside `uv` environments.
|
|
78
|
+
|
|
79
|
+
To run the complete automated test suite (including performance, memory, and stress testing):
|
|
80
|
+
```bash
|
|
81
|
+
uv run pytest -v -s
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 📄 License
|
|
85
|
+
|
|
86
|
+
Distributed under the MIT License. Developed by Fort66 (mamonov.1966@yandex.ru).
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# hashlist
|
|
95
|
+
|
|
96
|
+
Высокопроизводительная замена стандартного Python `list` с **O(1) скоростью поиска и разрешения индексов**, работающая на нативном C++17 бэкенде через `pybind11`.
|
|
97
|
+
|
|
98
|
+
## 🚀 Проблема и наше решение
|
|
99
|
+
|
|
100
|
+
В Python проверка наличия элемента в стандартном списке (`item in my_list`) или поиск его позиции (`my_list.index(item)`) занимает **O(N) линейное время**. По мере того как ваш набор данных растет до миллионов элементов, эти операции превращаются в критическое «узкое место» для процессора.
|
|
101
|
+
|
|
102
|
+
`hashlist` решает эту проблему за счет внедрения оптимизированной гибридной структуры данных. Она сочетает в себе быстрый нативный C++ `std::vector` (сохраняющий исходный порядок вставки, индексы и срезы) с продвинутой хэш-картой `std::unordered_map<ssize_t, std::vector<size_t>>` на уровне машинных инструкций.
|
|
103
|
+
|
|
104
|
+
Результат? Поиск, подсчет вхождений и извлечение индексов выполняются за **константное время O(1)**, что делает контейнер более чем в **6100 раз быстрее** стандартного Python-списка на тяжелых разнородных нагрузках.
|
|
105
|
+
|
|
106
|
+
## ✨ Особенности
|
|
107
|
+
|
|
108
|
+
- **⚡ Молниеносный поиск:** Константное время O(1) для оператора `in` независимо от размера списка.
|
|
109
|
+
- **🚀 Моментальное разрешение индексов:** В отличие от стандартных списков, метод `.index()` находит позицию элемента за O(1) благодаря непрерывному низкоуровневому отслеживанию бакетов.
|
|
110
|
+
- **🧠 100% всеядность и безопасность типов:** Надежно обрабатывает как стандартные хэшируемые типы (числа, строки), так и изменяемые нехэшируемые структуры (вложенные `list`, `dict`, `set`) с помощью детерминированного алгоритма рекурсивной «заморозки» на C++.
|
|
111
|
+
- **📦 Полноценная drop-in замена:** Полностью реализует протокол `MutableSequence` в CPython. Из коробки поддерживаются сложные срезы (`hl[0:5:2]`), отрицательная индексация, математические операторы (`+`, `*`, `+=`, `*=`) и глобальные встроенные функции `sorted()` и `reversed()`.
|
|
112
|
+
- **🪶 Оптимизация памяти:** Построена с использованием `__slots__` на уровне Python-оболочки. Пустой `hashlist` занимает всего **40 байт** памяти благодаря ленивой инициализации внутренних C++ структур.
|
|
113
|
+
|
|
114
|
+
## 📊 Бенчмарк производительности
|
|
115
|
+
|
|
116
|
+
Поиск сложного вложенного изменяемого словаря в самом конце перемешанной коллекции из **5 000 000** разнородных элементов:
|
|
117
|
+
|
|
118
|
+
| Тип контейнера | Время поиска (Секунды) | Алгоритмическая сложность | Прирост скорости |
|
|
119
|
+
| :--- | :--- | :--- | :--- |
|
|
120
|
+
| Стандартный Python `list` | ~0.14096335 сек | O(N) (Линейный проход) | Базовый уровень |
|
|
121
|
+
| **`hashlist` (C++ ядро)** | **0.00002302 сек** | **O(1) (Константная карта)** | **В 6123.5 раза быстрее** |
|
|
122
|
+
|
|
123
|
+
*Тестирование проводилось под Python 3.13.15 на операционной системе CachyOS (Arch Linux, компилятор GCC 16.2.1).*
|
|
124
|
+
|
|
125
|
+
## 🛠️ Спецификация поддерживаемых методов
|
|
126
|
+
|
|
127
|
+
`hashlist` полностью поддерживает все 12 канонических методов мутации и поиска стандартного списка со специальными архитектурными оптимизациями C++:
|
|
128
|
+
|
|
129
|
+
| Метод | Описание | Сложность | Преимущество C++ бэкенда |
|
|
130
|
+
| :--- | :--- | :--- | :--- |
|
|
131
|
+
| `hl.append(x)` | Добавляет элемент `x` в конец. Поддерживает вложенные структуры. | **O(1)** амортизировано | Эффективность нативного выделения памяти C++ |
|
|
132
|
+
| `hl.extend(iterable)` | Добавляет в конец все элементы из переданной последовательности. | **O(M)** | Быстрая последовательная пакетная вставка (зависит только от размера принесенных данных) |
|
|
133
|
+
| `hl.insert(i, x)` | Вставляет элемент `x` на позицию `i` со сдвигом вектора. | O(N-i) нативный сдвиг | Прямой сдвиг блока памяти на уровне машинных инструкций процессора |
|
|
134
|
+
| `hl.remove(x)` | Удаляет первое вхождение значения `x`. | **O(1) проверка поиска** + O(N) сдвиг | **Мгновенный вылет с ошибкой** за O(1), если элемента нет (без линейного сканирования вектора) |
|
|
135
|
+
| `hl.clear()` | Удаляет все элементы, полностью освобождая память. | **O(1)** | Моментальная очистка бакетов хэш-карты и деаллокация вектора |
|
|
136
|
+
| `hl.index(x)` | Возвращает целочисленный индекс первого вхождения `x`. | **🚀 O(1) константа** | **Мгновенное разрешение индекса** напрямую из карты бакетов без линейных переборов |
|
|
137
|
+
| `hl.pop(i=-1)` | Удаляет и возвращает элемент по индексу `i`. | **🔥 O(1) для извлечения с конца** / O(N-i) сдвиг | Активация оптимизации **Fast-Path** при дефолтном удалении последнего элемента |
|
|
138
|
+
| `hl.count(x)` | Возвращает количество повторений элемента `x` в списке. | **🚀 O(1) константа** | Молниеносное извлечение размера вектора индексов напрямую из хэш-карты |
|
|
139
|
+
| `hl.sort()` | Сортирует элементы на месте. Поддерживает `key` и `reverse`. | \(O(N \log N)\) | Быстрый проход алгоритма Timsort напрямую по непрерывному C-массиву |
|
|
140
|
+
| `hl.reverse()` | Разворачивает элементы списка на месте. | **O(N)** | Высокооптимизированная перестановка указателей через `std::reverse` |
|
|
141
|
+
| `hl.copy()` | Создает поверхностную копию контейнера. | **O(N)** | Быстрая нативная итерация и зеркалирование элементов |
|
|
142
|
+
|
|
143
|
+
## 💻 Быстрый старт
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from hashlist import hl, hashlist
|
|
147
|
+
|
|
148
|
+
# 1. Быстрое создание через фабричную функцию
|
|
149
|
+
my_list = hl(10, 20, 30, "яблоко")
|
|
150
|
+
|
|
151
|
+
# 2. Безопасное добавление сложных нехэшируемых структур
|
|
152
|
+
my_list.append({"имя": "Иван", "id": 66})
|
|
153
|
+
|
|
154
|
+
# 3. Моментальный O(1) поиск и извлечение индексов на уровне C++
|
|
155
|
+
print("яблоко" in my_list) # True
|
|
156
|
+
print(my_list.index("яблоко")) # 3 (Мгновенно за O(1)!)
|
|
157
|
+
print({"id": 66, "имя": "Иван"} in my_list) # True (Независимо от порядка ключей!)
|
|
158
|
+
|
|
159
|
+
# 4. Стандартные функции Python и срезы работают бесшовно
|
|
160
|
+
for item in reversed(my_list):
|
|
161
|
+
print(item)
|
|
162
|
+
|
|
163
|
+
sub_slice = my_list[1:4]
|
|
164
|
+
print(20 in sub_slice) # True
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 🏗️ Разработка и тестирование
|
|
168
|
+
|
|
169
|
+
Для автоматической компиляции и сборки расширения внутри изолированных окружений `uv` используются `scikit-build-core` и `pybind11`.
|
|
170
|
+
|
|
171
|
+
Чтобы запустить полный автоматизированный набор тестов (включая нагрузочное стресс-тестирование и бенчмарки скорости):
|
|
172
|
+
```bash
|
|
173
|
+
uv run pytest -v -s
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## 📄 Лицензия
|
|
177
|
+
|
|
178
|
+
Распространяется под лицензией MIT. Разработчик: Fort66 (mamonov.1966@yandex.ru).
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# ruff: noqa: I001
|
|
2
|
+
from collections.abc import MutableSequence
|
|
3
|
+
from .hashlist_backend import HashListCpp
|
|
4
|
+
|
|
5
|
+
class hashlist(MutableSequence):
|
|
6
|
+
__slots__ = ('_backend',)
|
|
7
|
+
|
|
8
|
+
def __init__(self, iterable=None):
|
|
9
|
+
"""Промышленный тип данных с высокопроизводительным ядром на C++"""
|
|
10
|
+
self._backend = HashListCpp()
|
|
11
|
+
if iterable is not None:
|
|
12
|
+
self.extend(iterable)
|
|
13
|
+
|
|
14
|
+
def append(self, item):
|
|
15
|
+
"""Добавляет элемент x в конец списка a."""
|
|
16
|
+
self._backend.append(item)
|
|
17
|
+
|
|
18
|
+
def extend(self, iterable):
|
|
19
|
+
"""Добавляет в конец a все элементы списка b."""
|
|
20
|
+
self._backend.extend(iterable)
|
|
21
|
+
|
|
22
|
+
def insert(self, index, item):
|
|
23
|
+
"""Вставляет элемент x на позицию i."""
|
|
24
|
+
self._backend.insert(index, item)
|
|
25
|
+
|
|
26
|
+
def remove(self, item):
|
|
27
|
+
"""Удаляет в a первый элемент, значение которого равно x."""
|
|
28
|
+
self._backend.remove(item)
|
|
29
|
+
|
|
30
|
+
def clear(self):
|
|
31
|
+
"""Удаляет все элементы из списка a и делает его пустым."""
|
|
32
|
+
self._backend.clear()
|
|
33
|
+
|
|
34
|
+
def index(self, item, *args):
|
|
35
|
+
"""Возвращает индекс элемента списка."""
|
|
36
|
+
if args:
|
|
37
|
+
items_list = [self[i] for i in range(len(self))]
|
|
38
|
+
return items_list.index(item, *args)
|
|
39
|
+
return self._backend.index_of(item)
|
|
40
|
+
|
|
41
|
+
def pop(self, index=-1):
|
|
42
|
+
"""Удаляет элемент по индексу и возвращает его."""
|
|
43
|
+
return self._backend.pop(index)
|
|
44
|
+
|
|
45
|
+
def count(self, item):
|
|
46
|
+
"""Считает, сколько раз элемент повторяется в списке за O(1)."""
|
|
47
|
+
return self._backend.count(item)
|
|
48
|
+
|
|
49
|
+
def sort(self, *args, **kwargs):
|
|
50
|
+
"""Сортирует список на месте."""
|
|
51
|
+
self._backend.sort(*args, **kwargs)
|
|
52
|
+
|
|
53
|
+
def reverse(self):
|
|
54
|
+
"""Разворачивает список на месте."""
|
|
55
|
+
self._backend.reverse()
|
|
56
|
+
|
|
57
|
+
def copy(self):
|
|
58
|
+
"""Создаёт поверхностную копию списка."""
|
|
59
|
+
new_hl = hashlist()
|
|
60
|
+
for i in range(len(self)):
|
|
61
|
+
new_hl.append(self[i])
|
|
62
|
+
return new_hl
|
|
63
|
+
|
|
64
|
+
# --- Новые методы поддержки глобальных функций sorted() и reversed() ---
|
|
65
|
+
def __reversed__(self):
|
|
66
|
+
"""Обеспечивает корректную работу глобальной функции reversed(hl)"""
|
|
67
|
+
# Возвращаем генератор, который идет с конца структуры к началу
|
|
68
|
+
for i in range(len(self) - 1, -1, -1):
|
|
69
|
+
yield self[i]
|
|
70
|
+
|
|
71
|
+
# --- Поддержка синтаксического контракта Python ---
|
|
72
|
+
def __len__(self):
|
|
73
|
+
return self._backend.__len__()
|
|
74
|
+
|
|
75
|
+
def __getitem__(self, index):
|
|
76
|
+
result = self._backend.getitem(index)
|
|
77
|
+
if isinstance(result, HashListCpp):
|
|
78
|
+
new_hl = hashlist()
|
|
79
|
+
new_hl._backend = result
|
|
80
|
+
return new_hl
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
def __setitem__(self, index, value):
|
|
84
|
+
self._backend.setitem(index, value)
|
|
85
|
+
|
|
86
|
+
def __delitem__(self, index):
|
|
87
|
+
self._backend.delitem(index)
|
|
88
|
+
|
|
89
|
+
def __contains__(self, item):
|
|
90
|
+
return self._backend.__contains__(item)
|
|
91
|
+
|
|
92
|
+
def __iter__(self):
|
|
93
|
+
"""Поддержка стандартного перебора для sorted()"""
|
|
94
|
+
for i in range(len(self)):
|
|
95
|
+
yield self[i]
|
|
96
|
+
|
|
97
|
+
def __add__(self, other):
|
|
98
|
+
if not hasattr(other, '__iter__'): return NotImplemented
|
|
99
|
+
result = self.copy()
|
|
100
|
+
result.extend(other)
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
def __iadd__(self, other):
|
|
104
|
+
if not hasattr(other, '__iter__'): return NotImplemented
|
|
105
|
+
self.extend(other)
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
def __mul__(self, value):
|
|
109
|
+
if not isinstance(value, int): return NotImplemented
|
|
110
|
+
result = hashlist()
|
|
111
|
+
for _ in range(value):
|
|
112
|
+
result.extend([self[i] for i in range(len(self))])
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
def __imul__(self, value):
|
|
116
|
+
if not isinstance(value, int): return NotImplemented
|
|
117
|
+
old_items = [self[i] for i in range(len(self))]
|
|
118
|
+
for _ in range(value - 1):
|
|
119
|
+
self.extend(old_items)
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
def __eq__(self, other):
|
|
123
|
+
if isinstance(other, hashlist):
|
|
124
|
+
if len(self) != len(other): return False
|
|
125
|
+
return [self[i] for i in range(len(self))] == [other[i] for i in range(len(other))]
|
|
126
|
+
if isinstance(other, list):
|
|
127
|
+
return [self[i] for i in range(len(self))] == other
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
def __repr__(self):
|
|
131
|
+
items_repr = [self[i] for i in range(len(self))]
|
|
132
|
+
return f"hashlist({repr(items_repr)})"
|
|
133
|
+
|
|
134
|
+
def hl(*args):
|
|
135
|
+
"""Удобная фабрика для создания hashlist через запятую"""
|
|
136
|
+
return hashlist(args)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hashlist"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A high-performance hybrid list with O(1) hashing speed for Python."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Fort66", email = "mamonov.1966@yandex.ru" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["scikit-build-core==0.10.7", "pybind11==2.13.6"]
|
|
14
|
+
build-backend = "scikit_build_core.build"
|
|
15
|
+
|
|
16
|
+
[dependency-groups]
|
|
17
|
+
dev = [
|
|
18
|
+
"pytest>=9.2.0",
|
|
19
|
+
]
|