Devpuff
Pricing
Log InStart Free
  1. Home
  2. Blog
  3. list.pop() in Python: remove and return, explained
Learn

list.pop() in Python: remove and return, explained

Python list pop() hands you the item it removes. remove() gives you nothing back. That one difference decides which of them to use.

By Max Arthur
Co-Founder & Content Marketer·October 10, 2026·8 Min read
White text reading 'How list.pop() works in Python' on a dark purple dotted background

list.pop() removes an item from a list and gives it back to you. That return value is the whole reason it exists, and it is what separates it from every other way of removing an item.

nums = [10, 20, 30]
last = nums.pop()
# last is 30, nums is now [10, 20]

With no argument it removes the last item. Pass an index to remove a specific one:

nums = [10, 20, 30]
nums.pop(0)    # returns 10, nums is [20, 30]

Every result on this page was run in Python 3.13 through Pyodide 0.28.3.

pop versus remove versus del

Three ways to take an item out, and they differ in what you give them and what you get back:

You give it It returns Use when
pop(i) a position the item You need the value
remove(x) a value None You know what to delete, not where
del lst[i] a position nothing You just want it gone

Measured, remove really does hand back nothing:

nums = [10, 20, 30]
result = nums.remove(20)
# result is None, nums is [10, 30]

That is the trap. removed = nums.remove(20) looks like it should give you 20 and gives you None instead, which then fails at the next line with a NoneType error. pop is the one that returns.

The other difference is what they search for. pop(1) removes whatever sits at position 1. remove(1) removes the first item equal to 1, wherever it is. With a list of numbers those read almost identically and do completely different things.

Negative indices work

pop() with no argument is the same as pop(-1), because -1 means the last item:

nums = [10, 20, 30]
nums.pop(-2)   # returns 20, nums is [10, 30]

Counting from the end is often clearer than computing len(nums) - 2.

The two errors

Both are IndexError, and the messages are usefully different:

[].pop()
# IndexError: pop from empty list

[1].pop(5)
# IndexError: pop index out of range

pop from empty list names the operation, which makes it easier to find than the generic version. It is the one that catches out queue and stack code where the list has run dry.

Guard by checking the list is not empty:

if nums:
    item = nums.pop()

An empty list is falsy, so if nums: reads well and covers it. Catching the error also works when running out is a normal outcome rather than a bug.

The performance detail nobody mentions

pop() from the end is fast. pop(0) from the front is not, and the difference matters as lists grow.

Removing the last item is a single operation. Removing the first means every remaining item shifts down one position, so the work grows with the length of the list. On a list of a million items, pop() is instant and pop(0) moves 999,999 items.

For a stack, where you add and remove at the same end, a list is exactly right:

stack = []
stack.append("a")
stack.append("b")
stack.pop()      # "b"

For a queue, where you add at one end and remove at the other, use deque:

from collections import deque

queue = deque(["a", "b", "c"])
queue.popleft()   # "a", and it is fast

deque is built for removal at both ends. If you find yourself writing pop(0) inside a loop, this is the swap to make.

Popping in a loop, safely

Because pop shortens the list, it interacts badly with for. This is the pattern to avoid:

for item in items:
    items.pop()        # skips elements

The loop tracks a position while the list shrinks under it, so it visits roughly half the items. The safe form is a while that re-checks the list each time:

while items:
    item = items.pop()
    handle(item)

while items: is True until the list is empty, so this drains it completely and cannot run off the end. The list is empty afterwards, which is either exactly what you wanted or a reason to work on a copy.

The same shape works for processing a queue that other code adds to while you work, which a for loop cannot handle at all.

pop on other types

The name is reused across Python's containers, and the behaviour differs in one important way.

Sets pop an arbitrary element, not the last one:

s = {"a", "b", "c"}
s.pop()      # 'c' on this run, and you cannot rely on which

Sets have no order, so pop takes whichever element is convenient. It is useful for "give me any item" and wrong for anything where you expect a particular one. Empty sets raise a KeyError rather than an IndexError:

set().pop()
# KeyError: 'pop from an empty set'

deque pops from either end, both quickly:

from collections import deque
dq = deque([1, 2, 3])
dq.popleft()   # 1
dq.pop()       # 3

That is the whole reason deque exists, and it is the fix for the pop(0) cost described above.

Dictionaries have pop too, with a bonus

dict.pop works the same way and takes a default, which lists do not offer:

d = {"a": 1}
d.pop("a")             # 1, and the key is gone
d.pop("zz", "fallback")  # "fallback", no error

That second argument makes it safe on a missing key. Without it you get a KeyError, the dictionary equivalent of the errors above.

There is no list version of that default, which is why removing from a list needs the if or the try.

Where pop earns its place

Processing a work queue. Take the next item and act on it in one step:

while tasks:
    task = tasks.pop()
    handle(task)

Moving an item between lists, which reads cleanly as one line:

done.append(todo.pop(0))

Undo stacks, where the last thing added is the first thing removed, and you need the value back to undo it.

What to avoid is pop inside a for loop over the same list. Removing items while iterating skips elements, and it is the subject of a longer warning in Python for loops. The while form above is safe because it re-checks the list each time rather than holding a position.

A worked example: an undo stack

pop and append together give you a stack in two lines, and an undo feature is the clearest illustration of why getting the value back matters.

history = []

def do(action):
    action.apply()
    history.append(action)

def undo():
    if not history:
        return None
    action = history.pop()
    action.revert()
    return action

append puts each action on the top. pop takes the most recent one off and hands it to you, which is exactly what undo needs, since it has to call revert on that specific action.

Written with remove or del this does not work: you would have to read the last item, then delete it, as two steps with a length check between them, and the list could be empty in between.

The if not history guard is the empty check from above. An empty list is falsy, so not history is True when there is nothing to undo, and returning early avoids the IndexError.

A redo stack is the same structure in reverse: redo.append(history.pop()) moves one action across, and history.append(redo.pop()) moves it back.

Three common mistakes

Assigning the result of remove.

nums = nums.remove(20)     # nums is now None

remove returns None. The list was modified correctly and then the variable was overwritten. pop is the one you can assign; remove and del are not.

Using pop where you meant remove.

nums.pop(20)      # position 20, probably an IndexError
nums.remove(20)   # the value 20

Both are valid Python and they do entirely different things. pop takes a position, remove takes a value. With a list of small integers they look identical on the page.

Popping inside a for loop. Covered above: the list shrinks while the loop tracks a position, so items are skipped. Use while lst: when draining, or build a new list.

pop versus slicing

When you want several items off the end rather than one, slicing is clearer than a loop of pops:

last_three = nums[-3:]     # read them
del nums[-3:]              # then remove them

That is two operations rather than three pops, and it does not need a loop. The trade is that slicing does not give you the items and remove them in one step, so pop is still the tool when you want exactly one item and want it back.

For taking a fixed number off the front, the same applies, and both are cheaper than repeated pop(0) because the shifting happens once rather than once per item.

Quick reference

lst.pop()        # remove and return the last item
lst.pop(0)       # remove and return the first (slow on big lists)
lst.pop(-2)      # counts from the end
lst.remove(x)    # remove first item equal to x, returns None
del lst[i]       # delete by position, returns nothing
lst.clear()      # empty the list

Want to work with lists where the errors are one click away? Start with the Python track.

Keep reading

More from the blog

White text reading 'What if name equals main does in Python' on a dark purple dotted background
October 27, 2026·8 min readLearn

What if __name__ == '__main__' actually does in Python

In Python, if __name__ == '__main__' runs code only when you run the file directly, not when it is imported. What __name__ holds and when you need it.

Read more
White text reading 'How to reverse a list in Python' on a dark purple dotted background
October 26, 2026·8 min readLearn

How to reverse a list in Python (and a string)

To reverse a list in Python, use reverse() to change it in place, [::-1] for a reversed copy, or reversed() to loop backwards. Which to use and why.

Read more
{ }
✦

Ready to write some code?

Put this into practice - start your first free lesson. No setup, no credit card.

Start learning free
Devpuff

Learn to code by doing. One tiny, playful lesson at a time.

Learn
ProgramsCoursesPricing
Company
AboutBlogResourcesAffiliates
Support
Help CenterContactStatus
Programs
Frontend DevelopmentFull-Stack DevelopmentReact DeveloperPython Developer
Courses
Advanced ReactAsync JavaScriptAsync PythonCSS BasicsCSS LayoutDSA Basics
Learn to Code
Learn JavaScriptLearn PythonLearn SQLLearn HTMLLearn CSSLearn ReactBrowse All Topics
Platform Comparisons
Devpuff vs CodecademyDevpuff vs Mimo
© 2026 Devpuff. All rights reserved.Privacy PolicyTerms and ConditionsCookies PolicyRefund Policy
Devpuff vs Sololearn
Devpuff vs freeCodeCamp
Read All Comparisons