Python's own queue implementation deque is implemented directly as C code in CPython. So it is very hard to match its performance with pure Python code. But this should not deter you from trying (unlike what @Cecil Curry said), because it makes you learn better.
Shaon shaonty implemented it as a linked list. I have implemented it using dictionaries as follows:
class Queue:
slots = ['_dict','_max_item','_min_item','_min_reshape_countdown_length', '_reshape_countdown']
def __init__(self):
#-------- Imlementation Constants ---------
self._min_reshape_countdown_length = 1000
#-------------------------------------------
self._dict = {}
self._max_item = None
self._min_item = None
self._reshape_countdown = self._min_reshape_countdown_length
def appendleft(self, item): #queue method, named to be consistent with deque
if not self._dict: #If the queue is empty
self._dict = {0:item}
self._max_item = self._min_item = 0
else:
self._dict[self._min_item - 1] = item
self._min_item -= 1
if self._reshape_countdown == 0:
self._reshape_countdown = max(len(self._dict), self._min_reshape_countdown_length)
self._reshape()
else:
self._reshape_countdown -=1
def pop(self): #dequeue method, named to be consistent with deque
if not self._dict:
raise IndexError('Queue is empty!')
item = self._dict[self._max_item]
del self._dict[self._max_item]
if not self._dict: #item was the last element
self._max_item = self._min_item = None
else:
self._max_item -= 1
if self._reshape_countdown == 0:
self._reshape_countdown = max(len(self._dict), self._min_reshape_countdown_length)
self._reshape()
else:
self._reshape_countdown -=1
return item
def _reshape(self):
if not self._dict: #if empty, no reshaping is needed
return
positives = max(0, self._max_item)
negatives = -min(0, self._min_item)
size = len(self._dict)
#If the data is evenly distributed around 0, then reshaping is not
#necessary.
if positives > size/3 and negatives > size/3:
return
center = (self._min_item + self._max_item)//2
self._dict = {key-center: value for key,value in self._dict.items()}
self._max_item = self._max_item - center
self._min_item = self._min_item - center
Note that this is not a good implementation to use for large queues, since creating large integers would be a problem. One can solve that problem by dividing the queue into blocks each having a dictionary of some fixed size.
I have compared the linked list implementation of Shaon shaonty, my implementation with dicts, and collections.deque with the following code:
import random
operations = []
queue_commands = 0
dequeue_commands = 0
for i in range(1000000):
if queue_commands > dequeue_commands:
operations.append(('q',random.random()) if random.random() < 1/2 else ('d',None))
else:
operations.append(('q',random.random()))
if operations[-1][0] == 'q':
queue_commands += 1
else:
dequeue_commands += 1
def test_case(queue):
for command, element in operations:
if command == 'q':
queue.appendleft(element)
else:
queue.pop()
the results are
%timeit test_case(Linked_Queue())
796 ms ± 2.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit test_case(Queue())
838 ms ± 1.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit test_case(deque())
120 ms ± 566 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
As I said, it is not easy to beat deque with raw Python code:
class Dummy:
def appendleft(self,item):
pass
def pop(self):
pass
%timeit test_case(Dummy())
176 ms ± 327 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)