Skip to content

Commit 59dd8e5

Browse files
Merge pull request #713 from realpython/python-deque
Sample code for the article on deque
2 parents d387c64 + f082bd5 commit 59dd8e5

File tree

8 files changed

+202
-0
lines changed

8 files changed

+202
-0
lines changed

python-deque/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Python's deque: Implement Efficient Queues and Stacks
2+
3+
This folder provides the code examples for the Real Python tutorial [Python's deque: Implement Efficient Queues and Stacks](https://realpython.com/python-deque/).

python-deque/custom_queue.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from collections import deque
2+
3+
4+
class Queue:
5+
def __init__(self):
6+
self._items = deque()
7+
8+
def enqueue(self, item):
9+
self._items.append(item)
10+
11+
def dequeue(self):
12+
try:
13+
return self._items.popleft()
14+
except IndexError:
15+
# Break exception chain to hide implementation details
16+
raise IndexError("dequeue from an empty queue") from None
17+
18+
def __len__(self):
19+
return len(self._items)
20+
21+
def __contains__(self, item):
22+
return item in self._items
23+
24+
def __iter__(self):
25+
yield from self._items
26+
27+
def __reversed__(self):
28+
yield from reversed(self._items)
29+
30+
def __repr__(self):
31+
return f"Queue({list(self._items)})"
32+
33+
34+
if __name__ == "__main__":
35+
queue = Queue()
36+
print("Enqueueing items with '.enqueue()'...")
37+
queue.enqueue(1)
38+
queue.enqueue(2)
39+
queue.enqueue(3)
40+
print(queue)
41+
print("Dequeueing one item with '.dequeue()'...")
42+
print(queue.dequeue())
43+
print(queue)

python-deque/page_history.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from collections import deque
2+
3+
page_history = deque(maxlen=3)
4+
5+
urls = ("https://google.com", "https://yahoo.com", "https://www.bing.com")
6+
7+
for url in urls:
8+
page_history.appendleft(url)
9+
10+
print(page_history)
11+
12+
page_history.appendleft("https://youtube.com")
13+
14+
print(page_history)
15+
16+
page_history.appendleft("https://facebook.com")
17+
18+
print(page_history)

python-deque/producer_consumer.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import logging
2+
import random
3+
import threading
4+
import time
5+
from collections import deque
6+
7+
logging.basicConfig(level=logging.INFO, format="%(message)s")
8+
9+
10+
def _wait_seconds(mins, maxs):
11+
time.sleep(mins + random.random() * (maxs - mins))
12+
13+
14+
def produce(queue, size):
15+
while True:
16+
if len(queue) < size:
17+
value = random.randint(0, 9)
18+
queue.append(value)
19+
logging.info("Produced: %d -> %s", value, str(queue))
20+
else:
21+
logging.info("Queue is saturated")
22+
_wait_seconds(0.1, 0.5)
23+
24+
25+
def consume(queue):
26+
while True:
27+
try:
28+
value = queue.popleft()
29+
except IndexError:
30+
logging.info("Queue is empty")
31+
else:
32+
logging.info("Consumed: %d -> %s", value, str(queue))
33+
_wait_seconds(0.2, 0.7)
34+
35+
36+
logging.info("Starting Threads...\n")
37+
logging.info("Press Ctrl+C to interrupt the execution\n")
38+
39+
shared_queue = deque()
40+
41+
threading.Thread(target=produce, args=(shared_queue, 10)).start()
42+
threading.Thread(target=consume, args=(shared_queue,)).start()

python-deque/tail.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from collections import deque
2+
3+
4+
def tail(filename, lines=10):
5+
try:
6+
with open(filename) as file:
7+
return deque(file, maxlen=lines)
8+
except OSError as error:
9+
print(f'Opening file "{filename}" failed with error: {error}')
10+
11+
12+
if __name__ == "__main__":
13+
print(tail("./README.md"))

python-deque/time_append.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from collections import deque
2+
from time import perf_counter
3+
4+
TIMES = 10_000
5+
NANOSECONDS_PER_SECOND = 1e9
6+
a_list = []
7+
a_deque = deque()
8+
9+
10+
def average_time(func, times):
11+
total = 0.0
12+
for i in range(times):
13+
start = perf_counter()
14+
func(i)
15+
# Convert to ns to improve readability
16+
total += (perf_counter() - start) * NANOSECONDS_PER_SECOND
17+
return total / times
18+
19+
20+
list_time = average_time(lambda i: a_list.insert(0, i), TIMES)
21+
deque_time = average_time(lambda i: a_deque.appendleft(i), TIMES)
22+
gain = list_time / deque_time
23+
24+
print(f"list.insert() {list_time:.6} ns")
25+
print(f"deque.appendleft() {deque_time:.6} ns ({gain:.6}x faster)")

python-deque/time_pop.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from collections import deque
2+
from time import perf_counter
3+
4+
TIMES = 10_000
5+
NANOSECONDS_PER_SECOND = 1e9
6+
a_list = [1] * TIMES
7+
a_deque = deque(a_list)
8+
9+
10+
def average_time(func, times):
11+
total = 0.0
12+
for _ in range(times):
13+
start = perf_counter()
14+
func()
15+
# Convert to ns to improve readability
16+
total += (perf_counter() - start) * NANOSECONDS_PER_SECOND
17+
return total / times
18+
19+
20+
list_time = average_time(lambda: a_list.pop(0), TIMES)
21+
deque_time = average_time(lambda: a_deque.popleft(), TIMES)
22+
gain = list_time / deque_time
23+
24+
print(f"list.pop(0) {list_time:.6} ns")
25+
print(f"deque.popleft() {deque_time:.6} ns ({gain:.6}x faster)")

python-deque/time_random_access.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from collections import deque
2+
from time import perf_counter
3+
4+
TIMES = 10_000
5+
MICROSECONDS_PER_SECOND = 1e6
6+
a_list = [1] * TIMES
7+
a_deque = deque(a_list)
8+
9+
10+
def average_time(func, times):
11+
total = 0.0
12+
for _ in range(times):
13+
start = perf_counter()
14+
func()
15+
# Convert to μs to improve readability
16+
total += (perf_counter() - start) * MICROSECONDS_PER_SECOND
17+
return total / times
18+
19+
20+
def time_it(sequence):
21+
middle = len(sequence) // 2
22+
sequence.insert(middle, "middle")
23+
sequence[middle]
24+
sequence.remove("middle")
25+
del sequence[middle]
26+
27+
28+
list_time = average_time(lambda: time_it(a_list), TIMES)
29+
deque_time = average_time(lambda: time_it(a_deque), TIMES)
30+
gain = deque_time / list_time
31+
32+
print(f"list {list_time:.6} μs ({gain:.6}x faster)")
33+
print(f"deque {deque_time:.6} μs")

0 commit comments

Comments
 (0)