š Key Concepts Overview
| Concept | One-Line Definition |
|---|---|
while loop |
Repeats code as long as a condition is True
|
while True |
Infinite loop ā needs break to stop |
break |
Immediately exits the loop |
continue |
Skips current iteration, moves to next |
| List | Ordered, mutable collection ā heterogeneous elements allowed |
| List Comprehension | One-line way to build a list using a loop + condition |
| List Mutability | Lists can be changed in place ā id() stays the same |
š Part 1 ā while Loop
The 3 Components (Critical Pattern)
# 1. Initialisation 2. Condition 3. Increment/Decrement
a = 1 # 1. Initialisation
while a <= 10: # 2. Condition
print('Devops')
a += 1 # 3. Increment
# Without increment ā INFINITE LOOP (condition never becomes False)
How it works: Condition is checked before each iteration. As soon as it's False, the loop stops. Miss the increment/decrement ā infinite loop (a real production hazard ā can hang a script or burn CPU).
while ā Practical Patterns
# Countdown (decrement)
a = 10
while a > 0:
print('Devops')
a -= 1
# Sum of 1 to 20
total = 0
a = 1
while a <= 20:
total += a
a += 1
print(total) # 210
# Product (factorial-style) of 1 to 20
product = 1
a = 1
while a <= 20:
product *= a
a += 1
print(product)
# Pattern using while + string repetition
str1 = 'Devops'
i = 0
while i < len(str1):
print(str1[i] * (i + 1))
i += 1
# D
# ee
# vvv
# oooo
# ppppp
# ssssss
for vs while ā When to Use Which
Use for
|
Use while
|
|---|---|
| You know the iterable / number of repetitions | You don't know how many times ā depends on a condition |
| Looping over list, string, range | Retry logic, polling, waiting for a state |
# DevOps: retry logic ā classic while True use case
max_attempts = 5
attempt = 0
while attempt < max_attempts:
print(f'Attempt {attempt+1}: Connecting to server...')
# if connection succeeds: break
attempt += 1
while True ā Infinite Loop Pattern
# Always True ā runs forever until break is hit
# Used for: retry logic, polling, menu-driven scripts, password validation
password = 'xyz1234'
p = input('Enter your password: ')
while True:
if p == password:
print('Password matched')
break # exits the loop
else:
print('Incorrect password. Try again')
p = input('Enter your password: ')
DevOps real use: polling an AWS resource until it reaches running state, retrying an API call until success, watching a deployment status.
# Simulated polling pattern (real-world shape)
status = 'pending'
checks = 0
while True:
checks += 1
print(f'Check #{checks}: status = {status}')
if status == 'running':
print('ā
Instance is up')
break
if checks >= 5:
print('ā Timeout waiting for instance')
break
# status = check_instance_status() # in real code
status = 'running' if checks == 3 else 'pending' # simulate
āļø Part 2 ā Transfer Statements: break & continue
break ā Exit the Loop Entirely
lst = [1, 37, 'Python', True, None, 'Devops', 38, 483, 38.438]
# Print elements until None is found, then stop
for i in lst:
if i == None:
break
else:
print(i)
# Output: 1, 37, Python, True (stops at None)
continue ā Skip Current Iteration Only
lst = [1, 37, None, 'Python', True, None, 'Devops', 38, 483, None, 38.438]
# Skip None values, but KEEP looping through the rest
for i in lst:
if i == None:
continue # skip this iteration, move to next
else:
print(i)
# Output: 1, 37, Python, True, Devops, 38, 483, 38.438 (all non-None values)
The Key Difference:
| Statement | Effect | Loop continues? |
|---|---|---|
break |
Stops the loop completely | ā No ā exits |
continue |
Skips just this one iteration | ā Yes ā moves to next |
break in Nested Loops ā Only Breaks the Inner Loop
lst = ['peter', 'piper', 'picked']
# break in inner loop only ā outer loop continues
for word in lst:
for char in word:
print(char)
break # breaks INNER loop only ā prints first char of each word
# p, p, p (first letter of each word)
# break in outer loop ā stops everything after first word
for word in lst:
for char in word:
print(char)
break # breaks OUTER loop ā only processes 'peter'
# p, e, t, e, r
# break in both ā exits after just ONE character total
for word in lst:
for char in word:
print(char)
break
break
# p (only)
ā ļø Critical for interviews: break/continue only affects the loop they're directly inside ā not outer loops.
š¦ Part 3 ā Lists Deep Dive
List Basics
# Heterogeneous ā any mix of types allowed
lst = [1, 37, None, 'Python', True, None, 'Devops', 38, 483, None, 38.438]
print(lst, type(lst))
List Operations
# Concatenation ā combine two lists
lst = [1, 2, 3, 4, 5]
lst1 = [6, 7, 8, 9, 10]
print(lst + lst1) # [1,2,3,4,5,6,7,8,9,10]
# ā ļø + does NOT modify in place ā must reassign
lst + [50]
print(lst) # unchanged! still [1,2,3,4,5]
lst = lst + [50] # ā
now it's updated
# ā Can't add a list and a non-iterable directly
# lst + 50 ā TypeError: can only concatenate list (not "int") to list
# Repetition
lst = [1, 2, 3, 4, 5]
print(lst * 5) # repeats the whole list 5 times
# Length
print(len(lst)) # 5
# Indexing
print(lst[2]) # 3rd element (0-indexed)
# Slicing
print(lst[::1]) # full copy, same order
print(lst[::-1]) # reversed list
Nested Lists & Deep Indexing
lst = [37, 'Python', 23+37j, True, [1, 36, 'Devops'],
[['Linux', 'AWS', 'Learnbay'], 73, 73.476, 51, 'Hello'],
'Sunday', 'weekend',
['Hello', ['hey', 'WFH', [37, 26.437, False], 'Functions']]]
print(lst[5][4]) # 'Hello' ā index 5 ā that sub-list ā index 4
print(lst[8][1][2][1]) # drilling 4 levels deep
# DevOps relevance: nested JSON/API responses look exactly like this
# e.g., ec2_response['Reservations'][0]['Instances'][0]['State']['Name']
Mutability ā The Core List Property
lst = [1, 2, 3, 4, 5]
print(id(lst)) # e.g. 140234...
lst[2] = 'Python' # modify in place ā item assignment
print(lst) # [1, 2, 'Python', 4, 5]
print(id(lst)) # SAME id ā list was modified, not recreated
Why this matters: Lists are mutable ā changing them doesn't create a new object. This is different from strings/tuples (immutable), where any "change" actually creates a brand-new object.
šļø Ways to Create a List
# Case 1 ā Direct literal (when you know elements)
lst = [1, 2, 34, 5, 6, 'python', True]
# Case 2 ā Using list() on an iterable
list('Python') # ['P','y','t','h','o','n']
list((10, 20, 30, 40)) # from a tuple ā [10,20,30,40]
list(range(12, 24)) # from a range ā [12,13,...,23]
list(input('Enter: ')) # from input string ā char by char list
# ā ļø list() on a single non-iterable-wrapped value vs a list with one item
list(['Python']) # ['Python'] ā already a list with 1 string
list('Python') # ['P','y','t','h','o','n'] ā string exploded char by char
# Case 3 ā List Comprehension (most DevOps-relevant!)
# Syntax: [expression for item in iterable if condition]
lst = [i for i in range(1, 11)] # [1,2,...,10]
lst = [i**2 for i in range(1, 21) if i % 2 == 0] # squares of even numbers 1-20
# Compare to the manual for-loop equivalent:
lst = []
for i in range(1, 11):
lst += [i]
# vs the one-liner:
lst = [i for i in range(1, 11)]
š ļø List Methods ā Full Reference
dir(lst) # shows all available methods on a list object
append() ā Add ONE Item to the End
lst = [1, 2, 3, 4, 5]
lst.append(30) # [1,2,3,4,5,30]
lst.append([30, 50]) # adds the WHOLE list as ONE nested item
# [1,2,3,4,5,30,[30,50]] ā ļø not flattened!
lst.append('Python') # adds as a single string element
# ā append() only takes ONE argument
# lst.append(30, 50) ā TypeError
extend() ā Add Items One-by-One (Flatten an Iterable In)
lst = [100, 200, 300, 400, 500]
lst.extend('Python') # adds each CHARACTER separately
print(lst) # [100,200,300,400,500,'P','y','t','h','o','n']
lst = [1, 2, 3, 4, 5]
lst.extend([30, 50]) # adds each element separately
print(lst) # [1,2,3,4,5,30,50]
append() vs extend() ā THE classic interview question:
| Method | Adds | Example |
|---|---|---|
append(x) |
x as a single element (even if x is a list) |
lst.append([1,2]) ā [...,[1,2]]
|
extend(x) |
each element of x individually |
lst.extend([1,2]) ā [...,1,2]
|
insert() ā Insert at a Specific Index
# lst.insert(index, value)
lst = [1, 37, None, 'Python', True]
lst.insert(2, 'Sunday') # inserts BEFORE index 2, shifts rest right
print(lst) # [1, 37, 'Sunday', None, 'Python', True]
lst.insert(-2, 'Python') # works with negative indexing too
count() ā Count Occurrences
lst = [1, 2, 3, 1, 2, 3, 1, 2, 3]
print(lst.count(2)) # 3
print(lst.count(1)) # 3
index() ā Find Position of First Occurrence
lst = [1, 2, 3, 1, 2, 3]
print(lst.index(2)) # 1 (first occurrence, left to right)
print(lst.index(20)) # ā ValueError: 20 is not in list
pop() ā Remove by Index (Returns the Removed Value)
# lst.pop(index) ā default removes the LAST item
lst = [100, 200, 300, 400, 500]
removed = lst.pop(2) # removes index 2 ā returns 300
print(removed) # 300
print(lst) # [100,200,400,500]
lst.pop() # removes LAST item by default
lst.pop(-2) # negative indexing works too
# ā Invalid index ā IndexError: pop index out of range
remove() ā Remove by VALUE (Not Index)
# lst.remove(value) ā removes FIRST occurrence, returns nothing
lst = [100, 200, 300, 400, 300, 500]
lst.remove(300) # removes the FIRST 300 only
print(lst) # [100,200,400,300,500]
# ā Value not in list ā ValueError
pop() vs remove() ā Interview Favourite:
| Method | Removes by | Returns | Error if missing |
|---|---|---|---|
pop(index) |
position/index | the removed value | IndexError |
remove(value) |
the value itself | None |
ValueError |
reverse() ā Reverses In Place
lst = [1, 2, 3, 4, 5]
lst.reverse()
print(lst) # [5,4,3,2,1] ā modifies original list
sort() ā Sorts In Place (Ascending by Default)
lst = [72, 48, 216, 4873, 462, 9]
lst.sort() # ascending
lst.sort(reverse=True) # descending
# Works on strings too (alphabetical / ASCII order)
lst = list('coiuhihcbciygdhs')
lst.sort()
# ā ļø sort() FAILS on mixed/heterogeneous types
lst = [1, 'Python', True, 38.438]
# lst.sort() ā TypeError: can't compare str and int
clear() ā Empty the List (Same Object, Same id())
lst = [1, 37, 'Python', True]
print(id(lst))
lst.clear()
print(lst) # []
print(id(lst)) # SAME id ā emptied in place, not a new object
copy() ā Create an Independent Copy (CRITICAL CONCEPT)
# ā WITHOUT copy() ā both variables point to the SAME list (same id)
lst = [1, 2, 3, 4, 5]
lst1 = lst # NOT a copy ā just another name for the same object
lst1[2] = 93
print(lst1) # [1,2,93,4,5]
print(lst) # [1,2,93,4,5] ā ļø original also changed!
# ā
WITH copy() ā independent list, different id
lst = [1, 2, 3, 4, 5]
lst1 = lst.copy() # new object, different id
lst1[2] = 93
print(lst1) # [1,2,93,4,5]
print(lst) # [1,2,3,4,5] ā
original untouched
This is one of the most important bugs to understand in Python. lst1 = lst does NOT copy ā it just creates a second reference to the exact same list in memory.
Manual String Split (Building Towards .split())
# Class built this manually to understand the LOGIC behind str.split()
str1 = 'Peter piper picked a pack of pickeled peppers'
lst = []
res = ''
for char in str1:
if char != ' ':
res += char
else:
lst.append(res)
res = ''
lst.append(res) # don't forget the last word!
print(lst)
# ['Peter', 'piper', 'picked', 'a', 'pack', 'of', 'pickeled', 'peppers']
# This is literally what str.split() does internally:
print(str1.split()) # same result, one line
āļø DevOps / Cloud Use Cases
# 1. Poll AWS instance status until running (while True pattern)
status = 'pending'
attempts = 0
while True:
attempts += 1
if status == 'running':
print('ā
Instance ready')
break
if attempts > 10:
print('ā Timeout ā instance not ready')
break
# status = get_instance_status() # real AWS SDK call
# 2. Retry failed deployment with break on success
max_retries = 3
attempt = 0
while attempt < max_retries:
attempt += 1
print(f'Deploy attempt {attempt}...')
success = (attempt == 2) # simulated
if success:
print('ā
Deployed')
break
else:
print('ā All retries failed')
# 3. Filter out failed health checks using continue
servers_status = ['ok', 'fail', 'ok', None, 'ok', 'fail']
for status in servers_status:
if status is None or status == 'fail':
continue
print(f'ā
Server status: {status}')
# 4. Build list of running instance IDs with list comprehension
instances = [
{'id': 'i-001', 'state': 'running'},
{'id': 'i-002', 'state': 'stopped'},
{'id': 'i-003', 'state': 'running'},
]
running_ids = [inst['id'] for inst in instances if inst['state'] == 'running']
print(running_ids) # ['i-001', 'i-003']
# 5. extend() to merge server lists from multiple regions
us_servers = ['web-us-1', 'web-us-2']
eu_servers = ['web-eu-1', 'web-eu-2']
all_servers = []
all_servers.extend(us_servers)
all_servers.extend(eu_servers)
print(all_servers)
# 6. copy() before modifying a shared config list
base_ports = [22, 80, 443]
web_ports = base_ports.copy()
web_ports.append(8080)
print(base_ports) # unaffected ā still [22, 80, 443]
print(web_ports) # [22, 80, 443, 8080]
ā Common Mistakes
| Mistake | Code | Fix |
|---|---|---|
Forgetting increment in while
|
while a<=10: print(a) (no a+=1) |
Infinite loop ā always increment/decrement |
lst + [x] doesn't modify list |
lst + [50] then expects lst changed |
Reassign: lst = lst + [50]
|
append() vs extend() confusion |
lst.append([1,2]) ā adds nested list, not 2 items |
Use extend() to flatten in |
lst1 = lst thinking it's a copy |
Modifying lst1 also changes lst
|
Use lst1 = lst.copy()
|
sort() on mixed types |
[1,'a',True].sort() |
TypeError ā sort only same-type lists |
pop() on empty/invalid index |
[].pop() |
IndexError ā check length first |
remove() on missing value |
lst.remove(999) when not present |
ValueError ā check in first |
break only exits ONE loop level |
Expecting break to exit nested loops fully |
Need a flag or function return for full exit |
šÆ Interview Points
"Difference between
append()andextend()?"
āappend()adds its argument as a single element (even a whole list becomes one nested item).extend()iterates through its argument and adds each element individually."Difference between
breakandcontinue?"
ābreakexits the loop entirely.continueskips only the current iteration and proceeds to the next one."Difference between
pop()andremove()?"
āpop(index)removes by position and returns the value; default removes the last item.remove(value)removes the first matching value and returns nothing."What happens when you do
lst1 = lstvslst1 = lst.copy()?"
ālst1 = lstmakeslst1point to the SAME object ā changes to one affect the other..copy()creates an independent object with a newid()."Why are lists mutable?"
ā Their contents can be changed after creation (id()stays the same) ā unlike strings/tuples where any change creates a new object."What's a list comprehension and why use it?"
ā A concise one-line way to build a list:[expr for item in iterable if condition]. More Pythonic and often faster than a manual for-loop + append."When would you use
while Trueover a regularwhile?"
ā When the exit condition is checked INSIDE the loop body rather than at the top ā e.g., retry logic, polling, "ask again until valid input."
š Knowledge Base ā Quick Revision
# āā WHILE LOOP āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
a = 1 # 1. init
while a <= 10: # 2. condition
...
a += 1 # 3. increment ā NEVER forget this
while True: # infinite ā needs break
if condition:
break
# āā BREAK / CONTINUE āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
break # exits the loop entirely
continue # skips current iteration, loop continues
# āā LIST CREATION āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
lst = [1, 2, 3] # literal
lst = list('abc') # from string ā ['a','b','c']
lst = list(range(5)) # from range
lst = [i for i in range(10)] # comprehension
lst = [i**2 for i in range(10) if i%2==0] # comprehension + filter
# āā LIST OPERATIONS āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
lst + lst2 # concatenation (new list, must reassign)
lst * 3 # repetition
len(lst) # length
lst[i] # indexing
lst[::-1] # reverse via slicing
# āā LIST METHODS (mutating, in-place) āāāāāāāāāāāāāāāāāāāāāāā
lst.append(x) # add ONE item to end
lst.extend(iterable) # add items individually
lst.insert(i, x) # insert before index i
lst.pop(i) # remove by index, RETURNS value (default: last)
lst.remove(x) # remove by VALUE, first match, returns None
lst.reverse() # reverse in place
lst.sort() # ascending; sort(reverse=True) for descending
lst.clear() # empty the list (same id)
lst.count(x) # occurrences of x
lst.index(x) # first index of x (ValueError if missing)
lst.copy() # independent shallow copy (new id)
# āā CRITICAL GOTCHA āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
lst1 = lst # ā same object ā NOT a copy
lst1 = lst.copy() # ā
independent copy
šļø Practice Questions
Easy
- Write a
whileloop that prints numbers from 10 down to 1. - Given
lst = [10, 20, 30, 40, 50], useappend()to add60, thenextend()to add[70, 80]. Print the list after each step. - Use a list comprehension to create a list of cubes (x³) for numbers 1 to 10.
Medium
- Write a
while Trueloop that keeps asking the user to enter a number until they enter a number greater than 100, then breaks and prints "Valid number entered." - Given
lst = [5, 10, None, 15, None, 20], write a loop usingcontinueto print only non-Nonevalues, and a separate loop usingbreakto print values until the firstNone. - Demonstrate the
lst1 = lstvslst1 = lst.copy()bug with code: create a list, assign it without.copy(), modify the copy, and show the original also changed. Then fix it using.copy().
DevOps-Focused
-
Retry Connector: Write a
whileloop that simulates connecting to a server. It should try up to 5 times. Use a variableconnected = Falsethat becomesTrueon the 3rd attempt (simulated). Print each attempt, and break with a success message once connected, or a failure message after 5 failed attempts. -
Server List Builder: You have two lists:
prod_servers = ['web-01','web-02']andstaging_servers = ['stg-01','stg-02']. Useextend()to build one combinedall_serverslist. Then use a list comprehension to createprod_onlycontaining only servers that start with'web'. Print both results.
Top comments (0)