1%
OnePercentDev
LearnRoadmapsCommunityPodcastConsult
Log inJoin OnePercentDev
1%
OnePercentDev

Build. Debug. Ship. AI-ready.
Disciplined daily learning for the AI era.

Navigation

  • Learn
  • Roadmaps
  • Community
  • Podcast
  • Consult

Platform

  • Python for AI
  • Create Account
  • Dashboard

Stay Updated

Get notified about new courses and content drops.

© 2026 OnePercentDev. All rights reserved.

Python for AI
Part 18
Lesson 18
16:58

Python Lists Advanced: sort vs sorted, enumerate, zip & Common Bugs | Python in Kannada | Part-18

Subscribe on YouTube

Up Next

18 / 58
  • 13:03

    Interview Rejection | Python Tuples Hidden Concept | Python in Kannada | Part-19

    Part 19

  • 1:04:30

    Sets & Hashing: SKIP ಮಾಡಿದ್ರೆ ಕೆಲಸ ಹೂಗ್ಗೆ! | Python in Kannada | Part-20

    Part 20

View all 58 lessons
View on GitHub

Part 18 — Lists Part 2 (Advanced Patterns)

Connecting to Part 17

Part 17 was foundation: what a list means, stack vs heap, why Python’s list is a dynamic array of references (not the same as a textbook linked list), indexing and slicing, mutability and stable id(), and copy vs alias (including shallow copies of nested lists). Part 17 deliberately stopped at a small method set (append, remove, pop, len) so the memory story could land first.

This part is advanced patterns: the rest of the list API (extend, insert, sort/reverse/search helpers), sorting strategies (sort vs sorted), enumerate() and zip(), and the bugs that still trip up experienced developers. We will continue using our DMart shopping cart examples from Part 17.


Adding Items — append vs extend vs insert

.append() — Add One Item to the End

my_cart = ["Rice", "Dal", "Oil"]
my_cart.append("Sugar")
print(my_cart)   # ['Rice', 'Dal', 'Oil', 'Sugar']

# Appending a list adds it as a SINGLE nested item
my_cart.append(["Salt", "Tea"])
print(my_cart)   # ['Rice', 'Dal', 'Oil', 'Sugar', ['Salt', 'Tea']]

.extend() — Add Multiple Items from Another Iterable

If you want to merge items from one cart directly into another without nesting:

wife_cart = ["Rice", "Dal"]
wife_cart.extend(["Sugar", "Tea"])
print(wife_cart)   # ['Rice', 'Dal', 'Sugar', 'Tea']  — each item added individually

.insert() — Add at a Specific Position

friend_cart = ["Rice", "Oil", "Tea"]

# Oh wait, we forgot Dal! Let's insert it at position 1.
friend_cart.insert(1, "Dal")
print(friend_cart)   # ['Rice', 'Dal', 'Oil', 'Tea']
MethodWhat It DoesExample
.append(x)Adds x as one item at the end["Rice"].append("Dal") → ["Rice", "Dal"]
.extend(iterable)Adds each element from iterable["Rice"].extend(["Dal", "Oil"]) → ["Rice", "Dal", "Oil"]
.insert(i, x)Inserts x at index i["Rice", "Oil"].insert(1, "Dal") → ["Rice", "Dal", "Oil"]

Details worth memorizing

  • .append(x) — takes exactly one object. That object can be anything (including another list), but it is always one slot at the end.
  • .extend(iterable) — takes one iterable and adds each element. Because a string is iterable character-by-character, cart.extend("Dal") adds 'D', 'a', 'l' as three items — usually you wanted append("Dal") or extend(["Dal"]).
  • .insert(i, x) — takes two arguments in this order: index, then value. cart.insert("Dal") raises TypeError (missing index). If i is greater than len(cart), Python inserts at the end (same effect as append). Negative i counts from the end (same idea as indexing).

Sorting

.sort() — Modify the List In-Place

prices = [149, 45, 210, 30, 85]
prices.sort()
print(prices)   # [30, 45, 85, 149, 210]

prices.sort(reverse=True)
print(prices)   # [210, 149, 85, 45, 30]

.sort() changes the original list. It returns None.

sorted() — Return a New Sorted List

prices = [149, 45, 210, 30, 85]
ordered = sorted(prices)

print(ordered)    # [30, 45, 85, 149, 210]
print(prices)     # [149, 45, 210, 30, 85]  — original unchanged

sorted() creates a new list. The original remains untouched.

Sorting Strings

sorted() can order a list of strings (whole words) or one string (character by character). These feel different — both use the same rule: compare characters using Unicode code points (for simple all-lowercase English words, that matches ordinary alphabetical order).

List of words — sorts the items as strings:

dmart_cart = ["Rice", "Dal", "Oil", "Apple"]
print(sorted(dmart_cart))   # ['Apple', 'Dal', 'Oil', 'Rice']

One string — sorted walks the characters and returns a new list of one-character strings; the original string is unchanged. There is no .sort() on str (strings are immutable).

product = "sugar"                    # all lowercase — easy to read as A→Z
letters = sorted(product)
print(letters)   # ['a', 'g', 'r', 's', 'u']
print(product)   # "sugar" — unchanged

# product.sort()   # AttributeError — str has no .sort()

Mixed capitals (for example "DMart") sort uppercase before lowercase in Unicode order, which can look odd until you use key=str.lower — we stick to one case here so the output matches what beginners expect.

When to Use Which

UseWhen
.sort()You want to modify the original list and do not need the unsorted version
sorted()You want a sorted copy while keeping the original order

This is a mutability decision — the same thinking from Part 17.

The Memory Proof: id()

To prove that sorted() creates a new object while .sort() modifies the existing one in place, we can check their memory IDs directly without even assigning them to variables.

dmart_cart = ["Rice", "Oil", "Dal"]

# 1. Print the original ID
print("Original ID:   ", id(dmart_cart))

# 2. Print the ID of the sorted() result directly (without storing it)
print("sorted() ID:   ", id(sorted(dmart_cart)))  # BRAND NEW ID!

# 3. Print the original ID again to prove it hasn't changed
print("Original ID:   ", id(dmart_cart))

If we try to print id(dmart_cart.sort()), it will print the ID for Python's built-in None object, because .sort() modifies the list in place and returns None.

Advanced Sorting: The key Parameter

Both .sort() and sorted() accept a key parameter. This lets you provide a function that transforms each item just for the purpose of comparison.

dmart_cart = ["Strawberry", "Fig", "Apple"]

# Sort by length of the string, not alphabetically!
dmart_cart.sort(key=len)
print(dmart_cart)   # ['Fig', 'Apple', 'Strawberry']

Sorting variants

  • .sort(*, key=None, reverse=False) and sorted(iterable, /, *, key=None, reverse=False) — optional key and reverse work the same way on both.
  • If items cannot be compared with each other (for example mixing numbers and strings with no key), Python raises TypeError.
  • Sorting is stable: items that compare equal keep their relative order (matters when using key=).

Reversing

checkout_items = ["Rice", "Dal", "Oil", "Sugar"]

# In-place reversal
checkout_items.reverse()
print(checkout_items)   # ['Sugar', 'Oil', 'Dal', 'Rice']

# New reversed list via slicing
original = ["Rice", "Dal", "Oil", "Sugar"]
rev = original[::-1]
print(rev)        # ['Sugar', 'Oil', 'Dal', 'Rice']
print(original)   # ['Rice', 'Dal', 'Oil', 'Sugar']  — unchanged

Three ways to reverse — pick by need

ApproachMutates original?What you get
.reverse()YesNone (list reversed in place)
reversed(lst)NoAn iterator; wrap with list(...) if you need a list
lst[::-1]NoA new list (shallow copy with reversed order)

Removing Items (Advanced)

While Part 17 introduced .remove() and .pop(), here are two more powerful ways to delete data from lists:

.clear() — Empty the List

Empties the entire list in-place, leaving it as []. It keeps the same list object in memory, just wiping its contents.

friend_cart = ["Rice", "Dal", "Oil"]
friend_cart.clear()
print(friend_cart)   # []

The del Statement

del is not a list method, but a built-in Python keyword. It is extremely powerful because it can delete items by index or even delete entire slices.

my_cart = ["Rice", "Dal", "Oil", "Sugar", "Tea"]

del my_cart[1]      # Removes "Dal"
print(my_cart)      # ['Rice', 'Oil', 'Sugar', 'Tea']

del my_cart[1:3]    # Removes "Oil" and "Sugar" (slice deletion)
print(my_cart)      # ['Rice', 'Tea']

del vs .clear() vs deleting the variable

  • lst.clear() or del lst[:] — empties the list; the name lst still refers to the same list object (id unchanged).
  • del lst — removes the variable name from scope; you must not use lst afterward (unless you assign it again).
  • del lst[i] / del lst[a:b] — removes elements (or a slice), list may still be non-empty.

Searching

.index() — Find Position of a Value

# Imagine you picked up multiple packets of Dal
dmart_cart = ["Rice", "Dal", "Oil", "Dal", "Sugar"]

print(dmart_cart.index("Dal"))   # 1 (finds the first occurrence)

# Optional start and stop (same slice-style bounds as slicing):
print(dmart_cart.index("Dal", 2))      # 3 — search from index 2 onward
print(dmart_cart.index("Dal", 0, 2))   # 1 — search only in [0, 2)

Raises ValueError if the value is not in the list (or not in the searched range).

.count() — Count Occurrences

print(dmart_cart.count("Dal"))   # 2
print(dmart_cart.count("Milk"))  # 0 — no error; missing means zero

enumerate() — Iterate with Index

A common beginner pattern:

my_cart = ["Rice", "Dal", "Oil"]

for i in range(len(my_cart)):
    print(i, my_cart[i])

The Pythonic way:

for index, item in enumerate(my_cart):
    print(index, item)

Output:

0 Rice
1 Dal
2 Oil

enumerate() returns pairs of (index, value) on each iteration.

Custom Start Index

for rank, item in enumerate(my_cart, start=1):
    print(f"Item {rank}: {item}")

Output:

Item 1: Rice
Item 2: Dal
Item 3: Oil

enumerate() is the professional way to iterate when you need both the index and the value. Never use range(len()) when enumerate() works.

Signature: enumerate(iterable, start=0) — only start is named; the iterable is the first argument.


zip() — Parallel Iteration

zip() combines two or more sequences element by element. This is perfect when you have related data spread across multiple lists:

items = ["Rice", "Dal", "Oil"]
prices = [450, 120, 210]

for item, price in zip(items, prices):
    print(f"{item}: ₹{price}")

Output:

Rice: ₹450
Dal: ₹120
Oil: ₹210

zip() stops at the shortest sequence:

items = ["Rice", "Dal", "Oil"]
prices = [450, 120]  # Missing the price for Oil!

for item, price in zip(items, prices):
    print(item, price)
# Only prints 2 pairs: (Rice, 450) and (Dal, 120)

With three or more lists, zip(a, b, c, ...) yields triples (or longer tuples) per step. In Python 3.10+, zip(..., strict=True) raises if lengths differ — handy when mismatch should be a bug, not silent truncation.

Combining enumerate and zip

items = ["Rice", "Dal", "Oil"]
prices = [450, 120, 210]

for rank, (item, price) in enumerate(zip(items, prices), start=1):
    print(f"{rank}. {item} — ₹{price}")

Output:

1. Rice — ₹450
2. Dal — ₹120
3. Oil — ₹210

List Slicing Creates New Lists

original_cart = ["Rice", "Dal", "Oil", "Sugar", "Tea"]
checkout_chunk = original_cart[1:4]

checkout_chunk[0] = "Premium Dal"
print(checkout_chunk)      # ['Premium Dal', 'Oil', 'Sugar']
print(original_cart)       # ['Rice', 'Dal', 'Oil', 'Sugar', 'Tea']  — unchanged

Slicing creates a shallow copy. Modifying the slice does not affect the original.


Stack Pattern

A stack follows Last In, First Out (LIFO) — the last item added is the first one removed. Imagine taking items out of your physical shopping cart to put them on the checkout counter.

checkout_stack = []

checkout_stack.append("Rice")
checkout_stack.append("Dal")
checkout_stack.append("Oil")

print(checkout_stack.pop())   # Oil (last in, first out)
print(checkout_stack.pop())   # Dal
print(checkout_stack)         # ['Rice']

Stacks are used for undo operations, function call tracking, and expression evaluation. The Python call stack (Part 6) works this way.


Common List Bugs

1. Modifying a List While Iterating

prices = [149, 45, 30, 210, 85, 120]

# Bad! We want to remove prices under 100
for p in prices:
    if p < 100:
        prices.remove(p)   # Dangerous — skips elements!

print(prices)   # [149, 30, 210, 120] — Notice how 30 was missed!

The safe approach — iterate over a copy:

prices = [149, 45, 30, 210, 85, 120]

for p in prices.copy():
    if p < 100:
        prices.remove(p)

print(prices)   # [149, 210, 120]

2. Confusing .sort() Return Value

prices = [149, 45, 210]
result = prices.sort()
print(result)    # None  — .sort() returns None, not the sorted list

Use sorted() if you need the sorted list as a value.

3. Forgetting .copy()

Covered in Part 17 — always use .copy() when you need an independent list.

4. .pop() with a bad index

cart = ["Rice"]
cart.pop(5)   # IndexError — nothing at index 5

.pop() with no argument removes and returns the last item. .pop(i) removes and returns the item at index i. Empty list: cart.pop() raises IndexError.

5. .remove() when the value is missing

cart = ["Rice", "Dal"]
cart.remove("Milk")   # ValueError — not in list

Complete List Methods Reference

Here is every built-in list method in one place:

MethodWhat It DoesReturns
.append(x)Adds x to the endNone
.extend(iterable)Adds each item from iterable to the endNone
.insert(i, x)Inserts x at index iNone
.remove(x)Removes first occurrence of x (raises ValueError if missing)None
.pop(i)Removes and returns item at index i (default: last)The removed item
.clear()Removes all itemsNone
.index(x)Returns index of first occurrence of x (raises ValueError if missing)int
.count(x)Returns number of times x appearsint
.sort()Sorts in place (ascending by default)None
.reverse()Reverses in placeNone
.copy()Returns a shallow copyNew list

Returns: Most methods that mutate the list return None (by design — you are not meant to chain them for a new list). .pop() is the exception: it returns the removed element. .index and .count() return an int.


Method vs built-in (how this part fits together)

KindExamples in this partMental model
List method.append, .extend, .insert, .sort, .reverse, …“Do this to this list” — call with . on the list.
Built-in functionsorted(), reversed(), len(), enumerate(), zip()“Do this to whatever you pass” — first argument is the data (or main input).
Statement / syntaxdel, slicing [start:stop:step]Not functions; part of Python grammar.

Why sorted but .sort? sorted(any_iterable) works on any iterable without turning it into a list first; .sort() only exists on lists and sorts in place. Same split for reversed() vs .reverse().


Where This Applies in Real Work

  • enumerate() — generating numbered lists in reports, tracking position while processing records, displaying ranked results in dashboards.
  • zip() — matching parallel data from different sources (item names + prices, dates + values, IDs + labels), common in data science when combining columns.
  • sorted() — displaying leaderboards, sorting API results, ordering data before display.
  • Stack pattern — undo/redo functionality, browser back button, expression parsers, function call management.
  • Modifying while iterating — one of the top 5 Python bugs in production. Code reviews flag this immediately.

Practice Assignment

Create a DMart checkout receipt:

  1. Define two lists:
cart_items = ["Rice", "Dal", "Oil", "Sugar", "Tea"]
cart_prices = [450, 120, 210, 50, 150]
  1. Use zip() to combine the two lists. Use zip(cart_prices, cart_items) so each row is a (price, item) tuple — with the price first, Python’s default tuple ordering sorts by price when you call sorted(..., reverse=True), and you do not need a key= function (no lambda yet — that lands in Part 28; list comprehensions in Part 23).
  2. Use sorted() (or .sort() on a list you built) so the most expensive line appears first.
  3. Use enumerate() with start=1 to print a numbered receipt:
1. Rice — ₹450
2. Oil — ₹210
3. Tea — ₹150
4. Dal — ₹120
5. Sugar — ₹50

Save as src/receipt.py.

If you are stuck — try the steps above first; the whole script is only a few lines after the two lists. Core loop:

rows = list(zip(cart_prices, cart_items))
ordered = sorted(rows, reverse=True)
for rank, (price, item) in enumerate(ordered, start=1):
    print(f"{rank}. {item} — ₹{price}")

Next: Part 19 — Tuples and Unpacking. The immutable sibling of lists. Tuples are simpler but play a crucial role in Python — from function returns to dictionary keys.

Lists Deep Dive: Memory, Mutability, Indexing & Shallow Copy Explained | Python in Kannada | Part-17Interview Rejection | Python Tuples Hidden Concept | Python in Kannada | Part-19

Up Next

18 / 58
  • 13:03

    Interview Rejection | Python Tuples Hidden Concept | Python in Kannada | Part-19

    Part 19

  • 1:04:30

    Sets & Hashing: SKIP ಮಾಡಿದ್ರೆ ಕೆಲಸ ಹೂಗ್ಗೆ! | Python in Kannada | Part-20

    Part 20

View all 58 lessons

GitHub Notes

View Part 18 notes on GitHub
GitHub Notes
View Part 18 notes on GitHub
PrevNext
26:07
26:07

Dictionaries: ಕಲಿಯದೆ JOB ಸಿಗಲ್ಲ! | Python in Kannada | Part-21

Part 21

Dictionaries: ಕಲಿಯದೆ JOB ಸಿಗಲ್ಲ! | Python in Kannada | Part-21

Part 21

  • 15:19

    Dictionaries Advance: Nested Data, APIs, Counter, defaultdict & JSON | Python in Kannada | Part-22

    Part 22

  • 15:19

    Dictionaries Advance: Nested Data, APIs, Counter, defaultdict & JSON | Python in Kannada | Part-22

    Part 22

  • 14:39

    Comprehensions: FOR LOOP ಬೇಡ, ONE LINE ಸಾಕು! | Python in Kannada | Part-23

    Part 23

  • 14:39

    Comprehensions: FOR LOOP ಬೇಡ, ONE LINE ಸಾಕು! | Python in Kannada | Part-23

    Part 23

  • 25:05

    1000 LINES COPY? ಒಂದೇ FUNCTION ಸಾಕು! | Python in Kannada | Part-24

    Part 24

  • 25:05

    1000 LINES COPY? ಒಂದೇ FUNCTION ಸಾಕು! | Python in Kannada | Part-24

    Part 24

  • 12:18

    AI AGENTS BUILD ಮಾಡ್ಬೇಕಾ? ಮೊದ್ಲು ಇದು ಕಲಿರಿ! | *args, **kwargs & Defaults | Part-25

    Part 25

  • 12:18

    AI AGENTS BUILD ಮಾಡ್ಬೇಕಾ? ಮೊದ್ಲು ಇದು ಕಲಿರಿ! | *args, **kwargs & Defaults | Part-25

    Part 25

  • 17:08

    Recursion: LOOP ಗೆ ಆಗದ PROBLEM ಇದಕ್ಕೆ EASY! 💡| Python in Kannada | Part-26

    Part 26

  • 17:08

    Recursion: LOOP ಗೆ ಆಗದ PROBLEM ಇದಕ್ಕೆ EASY! 💡| Python in Kannada | Part-26

    Part 26