The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Use a Python list as a stack when you need last-in, first-out (LIFO) behavior at one end: call append() to push and pop() with no index to remove the top item. In CPython, both operations at the list’s right-hand end are O(1). Choose collections.deque instead when you also need efficient operations at the left end or a deliberately double-ended API.
What a stack means in Python
A stack is an abstract data type in which the most recently added item is the first one removed. This is called last-in, first-out (LIFO). A useful physical model is a pile of plates: you place a plate on top and take the top plate off before touching anything underneath.
Python’s official tutorial describes lists as an easy way to use a stack: the last element added is the first element retrieved. Keep the top at the list’s right-hand end. That convention matters because removing from the left side requires moving the remaining elements.
The basic operations
- Push: add an item with
stack.append(value). - Pop: remove and return the top item with
stack.pop(). - Peek: inspect the top item with
stack[-1]without removing it. - Empty check: use
if not stack. - Size: use
len(stack).
Implement a stack with a list
For a stack that only pushes and pops at one end, a list is usually the clearest implementation:
#1 Best Overall
stack = []
stack.append("first") # push
stack.append("second") # push
item = stack.pop() # returns "second"
print(item) # second
print(stack) # ['first']
The rightmost value is the top. After the two pushes, the logical order from bottom to top is first, then second. The call to pop() therefore returns second.
Peeking without removing
if stack:
top = stack[-1]
print(f"Top item: {top}")
else:
print("The stack is empty")
Indexing an empty list with [-1] raises IndexError, so check the stack first unless that exception is the behavior you want.
Processing until empty
stack = ["parse", "validate", "save"]
while stack:
task = stack.pop()
print(f"Processing {task}")
This processes save, then validate, then parse. The loop’s truth test is false once the list contains no items.
Time complexity and the correct end to use
The Python complexity reference records list.append as O(1). It records list.pop(k) as O(n-k), so removing the final element with pop() is O(1) in CPython. These figures describe CPython’s built-in list implementation; another Python implementation can have different internal costs. See the official time-complexity table.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRank #2
| Operation | Code | Typical CPython cost | Effect |
|---|---|---|---|
| Push at top | append(value) |
O(1) | Adds an item at the right-hand end. |
| Pop at top | pop() |
O(1) | Removes and returns the final element. |
| Peek at top | [-1] |
O(1) | Reads without removing. |
Pop at index k |
pop(k) |
O(n-k) | Elements after k must be shifted. |
| Pop at bottom | pop(0) |
O(n) | All remaining elements may need to move. |
Do not implement a stack by inserting at index zero and removing with pop(0). The CPython documentation explains that these operations require O(n) memory movement because the list’s underlying representation must shift its elements; the relevant discussion is in CPython’s collections documentation.
When a deque is a better choice
collections.deque is a double-ended queue. It provides efficient operations at both ends: append, appendleft, pop, and popleft. The standard-library documentation defines and lists these operations in the collections reference.
from collections import deque
stack = deque()
stack.append("first")
stack.append("second")
print(stack.pop()) # second
print(stack[-1]) # first, without removing
For a pure one-ended stack, this has no required correctness advantage over a list. Pick a deque when the same structure may later serve both ends, when callers benefit from an explicit double-ended type, or when your design already uses deque operations elsewhere.
| Question | List | deque |
|---|---|---|
| Push and pop on the right | Yes; natural syntax | Yes |
| Efficient operations on the left | No; pop(0) and insert(0, value) are O(n) |
Yes; use popleft() and appendleft() |
| Smallest, simplest stack code | Usually | Requires an import |
| Explicit two-ended API | Not specialized | Yes |
| Best default for one-end LIFO | Usually | Use when two-end behavior may matter |
Wrap the storage in a custom Stack class
A wrapper is useful when code should not mutate the underlying container directly, or when you need validation, logging, metrics, or a domain-specific exception later. The method names are an API design choice; Python does not require a particular custom class.
class Stack:
def __init__(self):
self._items = []
def push(self, value):
self._items.append(value)
def pop(self):
return self._items.pop()
def peek(self):
return self._items[-1]
def is_empty(self):
return not self._items
def __len__(self):
return len(self._items)
work = Stack()
work.push("compile")
work.push("test")
print(work.peek()) # test
print(work.pop()) # test
print(len(work)) # 1
Decide what empty operations mean
The list-backed class above deliberately preserves the container behavior: pop() on an empty stack raises IndexError, and peek() on an empty stack also raises IndexError. That is often preferable because an invalid operation is immediately visible.
Other applications may want a different contract. You could return None, return a caller-supplied default, or raise a domain-specific exception. Make the choice explicit and document it; silently returning None can hide a missing task when None is also a valid value.
A guarded API
class SafeStack:
def __init__(self):
self._items = []
def push(self, value):
self._items.append(value)
def pop(self):
if not self._items:
raise LookupError("cannot pop from an empty stack")
return self._items.pop()
def peek(self):
if not self._items:
raise LookupError("cannot peek at an empty stack")
return self._items[-1]
def is_empty(self):
return not self._items
def __len__(self):
return len(self._items)
Use a custom exception instead if callers need to distinguish stack exhaustion from other lookup failures. Do not add thread-safety merely because the class has a wrapper; synchronization is a separate design requirement.
Choosing between list, deque, and a wrapper
- Use a list when the only operations are push, pop, peek, emptiness, and length at the right-hand end.
- Use a deque when you need efficient work at both ends or want that capability represented in the type.
- Use a wrapper when you need to restrict mutation, validate values, expose domain language, or control empty-stack errors.
For all choices, keep LIFO behavior visible in tests. A minimal test should push several distinct values, assert that pops return them in reverse insertion order, verify that peeking does not change the length, and check the documented empty behavior.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →def test_stack_order():
stack = Stack()
stack.push("a")
stack.push("b")
stack.push("c")
assert stack.peek() == "c"
assert len(stack) == 3
assert stack.pop() == "c"
assert stack.pop() == "b"
assert stack.pop() == "a"
assert stack.is_empty()
def test_empty_stack_errors():
stack = Stack()
try:
stack.pop()
except IndexError:
pass
else:
raise AssertionError("pop() should fail on an empty stack")
Common mistakes and fixes
Removing from the wrong end
Symptom: items come out in insertion order. Cause: code uses pop(0) or removes the first element. Fix: keep the top at the right and call pop().
Peeking with no empty check
Symptom: an unexpected IndexError. Cause: stack[-1] was evaluated after all items were removed. Fix: check if stack, catch the documented exception, or provide a wrapper method with an explicit empty policy.
Mutating a private list
Symptom: callers bypass validation or break invariants. Cause: a wrapper exposes _items or returns it directly. Fix: expose operations such as push, pop, and peek, and return copies or read-only views when inspection is necessary.
Assuming every Python implementation has identical costs
Symptom: a performance assumption fails after changing interpreters. Cause: CPython’s documented list costs were treated as a language-wide guarantee. Fix: qualify complexity claims as CPython behavior and measure the target implementation for performance-critical workloads.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
Performance, memory, and reliability notes
Both lists and deques hold references to Python objects; pushing an object does not copy the object itself. A list can grow its internal storage to make repeated right-end appends efficient, but its capacity and allocation details are implementation behavior rather than a stack API guarantee. A deque is organized for double-ended operations and is a better fit when left-end work is part of the workload.
Neither container automatically limits size. If input can grow without bound, enforce a maximum length or reject new values before memory usage becomes a reliability problem. A bounded deque can discard or reject entries according to the behavior you choose, but that policy must be explicit because discarded stack entries change the semantics.
Stacks are not queues: a queue removes the oldest item, whereas a stack removes the newest. If you need producer-consumer FIFO behavior, choose a queue-oriented design rather than reversing a stack’s meaning.
Or skip the browser setup
If you are creating documentation or visual regression images for a stack demo, ScreenshotNeo can capture the rendered page through one request instead of requiring you to configure a browser. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
The API supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets or custom viewports, retina scale, PDFs, custom CSS and JavaScript, clicks before capture, selector or network-idle waits, request blocking, custom headers and cookies, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification.
See the ScreenshotNeo API documentation for request options. This example captures a page as WebP:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And in Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to start without a card.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

