NetAuto Academy

Python for Network Engineers

Functions & Reusable Automation Logic

Beginner13 min read

What you'll learn

  • Why repeating the same logic in multiple places is a problem worth avoiding
  • How def, parameters, and return work together to define a function
  • How to call the same function on different devices
  • The difference between defining a function once and reusing it many times
  • How to recognize whether a piece of logic deserves to become a function
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

The problem functions solve

Picture checking NTP sync for three different devices, without functions — the same handful of lines, typed out three separate times, once per device, each with a slightly different variable name. It works. It's also exactly the kind of repetition this entire curriculum has been arguing against since "Start Here": the same logic, copy-pasted, is fragile — fix a bug in one copy and the other two still have it.

A function is how Python lets you package a repeatable task into a named, reusable block: define the logic once, then reuse it as many times as you need, for as many devices as you have.

Without a function
  1. Check SW1NTP logic written out in full
  2. Check SW2same NTP logic, copy-pasted
  3. Check SW3same NTP logic, copy-pasted again
With a function
  1. def check_ntp(device):logic defined once
  2. check_ntp(SW1)
  3. check_ntp(SW2)
  4. check_ntp(SW3)
Quick checkConcept check
What problem does defining a function solve, compared to copy-pasting the same logic for every device?

def, parameters, and return

check_ntp.py
def check_ntp(device):
  if device["ntp_synced"]:
      return "synced"
  else:
      return "out of sync"

def starts a function definition; check_ntp is its name. (device) in parentheses is a parameter — a placeholder for whatever value gets passed in when the function is called. return hands a value back to whatever called the function, and immediately ends the function.

Three pieces, worth naming individually:

  • def check_ntp(device): — defines a function named check_ntp that expects one input, called device inside the function.
  • device (the parameter) — a placeholder name. It doesn't refer to any specific device until the function is actually called with one.
  • return "synced" — sends a value back out of the function, so whatever called it can use the result.

Calling the function

Defining a function doesn't run it — you have to call it, passing in an actual value for the parameter:

run_checks.py
devices = [
  {"hostname": "SW1", "ntp_synced": True},
  {"hostname": "SW2", "ntp_synced": False},
]

for device in devices:
  result = check_ntp(device)
  print(f"{device['hostname']}: {result}")

Each time through the loop, check_ntp(device) calls the function with the current device's dictionary. Inside the function, the parameter 'device' now refers to whichever dictionary was passed in. Output: SW1: synced, then SW2: out of sync — same function, two different results, because the input was different each time.

This is the payoff: check_ntp was defined exactly once, but it's being reused for every device in the loop, each time acting on that device's own data. Same shape as combining a loop with a condition, one lesson ago — a loop repeats; a function packages what gets repeated so it can be reused anywhere, not just inside this one loop.

Quick checkConcept check
In 'def check_ntp(device):', what is 'device'?
Quick checkPredict output
Given the check_ntp function above, what does check_ntp({"hostname": "SW3", "ntp_synced": True}) return?

Which function is actually reusable?

Not every function is equally reusable — it depends on whether it relies on a parameter, or on a value baked into the code:

reusable_vs_not.py
# Not reusable — hardcoded to one device
def check_sw1_ntp():
  device = {"hostname": "SW1", "ntp_synced": True}
  return "synced" if device["ntp_synced"] else "out of sync"

# Reusable — works for any device passed in
def check_ntp(device):
  return "synced" if device["ntp_synced"] else "out of sync"

check_sw1_ntp only ever checks one hardcoded device — calling it twice gives the same result twice, no matter what. check_ntp takes device as a parameter, so calling it with a different dictionary each time produces a different, correct result for that device. Only the second one is genuinely reusable.

Quick checkScenario
Which of the two functions above (check_sw1_ntp or check_ntp) would actually work correctly if you called it on SW2 and SW3, not just SW1?

Not Netmiko yet

check_ntp here doesn't actually connect to anything — it just looks at a value already sitting in a dictionary. That's deliberate: this lesson is about the function concept, not about SSH sessions. A later module (Netmiko Fundamentals) is where a function like this starts actually talking to a real device.

Key takeaways

  • A function packages a repeatable task into a named, reusable block — defined once with def, used as many times as needed.
  • Parameters are placeholder names for whatever value gets passed in when the function is called; they have no value until then.
  • return sends a value back out of a function, so the caller can use the result.
  • A function that takes its input as a parameter is genuinely reusable across every device; one with a hardcoded value inside it isn't.
  • Functions and loops solve related but different problems: a loop repeats an action; a function packages the action so it can be reused anywhere, not just inside one loop.