Python › Programming Fundamentals

Functions and error handling

5 min read Beginner 5 sections

Two ideas finish the fundamentals and turn loose scripts into real tools. Functions let you write a check once and reuse it on thousands of inputs. Error handling lets that check survive the messy reality of dead hosts and malformed responses without crashing and losing all your progress.

You'll learn to

  • Write a function that takes input and returns a result
  • Understand local vs global scope
  • Wrap risky code so one failure doesn't kill the script

Functions

A function is a reusable, named block of code.

import requests

def is_alive(url):                 # def names it; url is a PARAMETER (an input)
    try:
        r = requests.get(url, timeout=5)
        return r.status_code       # send a value back to the caller
    except requests.RequestException:
        return None                # couldn't connect

code = is_alive("https://example.com")   # CALL it with an ARGUMENT
if code == 200:
    print("alive")

Walk through it. def is_alive(url): defines a function taking one parameter, url. return sends a value back to whoever called it — that value is what code receives. You call the function by writing its name with an argument in parentheses. Now you can check liveness on one URL or ten thousand, with the logic written exactly once.

Scope: where variables live

A variable created inside a function exists only inside it — that’s local scope. The r in is_alive above can’t be used outside the function. Variables defined at the top level of your file are global and visible everywhere.

Error handling: surviving the real world

When something goes wrong, Python raises an exception and, by default, crashes the whole script. try/except lets you catch the error and keep going.

import requests

try:
    r = requests.get("https://example.com", timeout=5)
    r.raise_for_status()           # raise an error on 4xx/5xx responses
    data = r.json()                # may fail if the body isn't JSON
except requests.Timeout:
    print("timed out")             # handle a specific error
except requests.RequestException as e:
    print(f"request failed: {e}")  # handle any requests error; e holds details
except ValueError:
    print("response wasn't JSON")
finally:
    print("done")                  # always runs, error or not

You put risky code in try, and handle failures in except blocks. You can catch specific errors (a timeout, a non-JSON body) or a general one. as e captures the error object so you can inspect it. finally always runs — useful for cleanup.

Checkpoint

Your scan loops over 5000 URLs. On URL number 3000, one host hangs and times out. Without error handling, what happens — and how does try/except fix it?

Try it yourself

Write a function fetch_title(url) that requests a URL with a timeout, and returns the first 60 characters of the response text — but wrapped in try/except so that if the request fails it returns the string "[unreachable]" instead of crashing. Then imagine calling it in a loop over a list of URLs.

Summary

A function packages logic under a name: it takes parameters, does work, and returns a result, so you write a check once and reuse it everywhere. Variables inside a function are local (invisible outside); pass data in via parameters and out via return. Error handling with try/except catches exceptions so one failure doesn’t crash the whole run — essential the moment you loop over many real, messy targets.

Key takeaways

  • def name(params): defines a function; return sends a value back.
  • Local variables live only inside their function — that’s scope.
  • try/except catches errors so your script survives bad inputs.
  • Robust tools wrap every risky operation; one dead host shouldn’t end a scan.

Quick quiz

That completes the programming fundamentals. Next module, you put it all to work: the requests library turns Python into a web-testing tool.

Was this lesson helpful?