Control Flow & Functions
Master if/elif/else, for and while loops, and write reusable functions with arguments, return values, and default parameters.
Learning Objectives
- → Use if/elif/else for conditional branching
- → Iterate with for loops and while loops
- → Use break, continue, and else clauses on loops
- → Define functions with positional and keyword arguments
- → Return values and understand scope
Conditionals
port = 443
if port == 80:
print("HTTP")
elif port == 443:
print("HTTPS")
elif port == 22:
print("SSH")
else:
print(f"Unknown service on port {port}")
Comparison operators: ==, !=, <, >, <=, >=
Logical: and, or, not
if port > 1024 and port < 65535:
print("Ephemeral port range")
For Loops
ports = [22, 80, 443, 8080]
for p in ports:
print(f"Checking port {p}...")
# Range
for i in range(1, 5): # 1 2 3 4
print(i)
# enumerate() gives index + value
for idx, p in enumerate(ports, start=1):
print(f"{idx}. Port {p}")
While Loops
attempts = 0
max_retries = 3
while attempts < max_retries:
response = input("Password: ")
if response == "secret":
print("Access granted")
break
attempts += 1
else:
print("Too many failed attempts")
The else clause on a loop runs when the loop finishes without hitting break.
Loop Control
for port in range(1, 1025):
if port == 25:
continue # skip SMTP
if port > 100:
break # stop scanning
print(port)
Functions
def scan_port(host, port, timeout=1):
"""Returns True if port is open."""
import socket
s = socket.socket()
s.settimeout(timeout)
try:
s.connect((host, port))
return True
except (socket.timeout, ConnectionRefusedError):
return False
finally:
s.close()
result = scan_port("127.0.0.1", 22)
print("Open" if result else "Closed")
Key features:
- def keyword, snake_case name
- Default argument: timeout=1
- Docstring (first string inside function)
- Returns a value
Multiple Return Values
def parse_endpoint(endpoint):
host, _, port = endpoint.rpartition(":")
return host, int(port)
host, port = parse_endpoint("192.168.1.1:8080")
print(host, port) # 192.168.1.1 8080
Scope
threshold = 1000 # global
def check_port(p):
label = "high" if p > threshold else "low" # reads global threshold
return label
# Use 'global' keyword to modify a global from inside a function (avoid if possible)
Lambda Functions
ports = [443, 22, 80, 8080, 3306]
ports.sort(key=lambda p: p) # sort ascending
high = list(filter(lambda p: p > 1024, ports)) # [8080, 3306]
Recursion Example
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
Mini Project – Port Range Scanner
import socket
def is_open(host, port, timeout=0.5):
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except OSError:
return False
def scan_range(host, start, end):
open_ports = []
for port in range(start, end + 1):
if is_open(host, port):
open_ports.append(port)
print(f" OPEN → {port}")
return open_ports
if __name__ == "__main__":
host = input("Host: ")
start = int(input("Start port: "))
end = int(input("End port: "))
results = scan_range(host, start, end)
print(f"\nFound {len(results)} open ports.")
Write a function classify_port(port) that returns 'well-known' for ports 0-1023, 'registered' for 1024-49151, and 'dynamic' for 49152-65535.
What keyword starts a conditional branch in Python?
What operator checks equality in Python?
Write a while loop that counts down from 10 to 1, printing each number. Then rewrite it as a for loop using range().
What does range(5) produce?
What keyword skips to the next loop iteration?
Implement the scan_range() mini project from the lesson. Test it against localhost ports 1-1024.
What socket exception is raised when a connection is refused?