Instiq
Chapter 6 · Automation and Artificial Intelligence·v1.0.0·Updated 7/21/2026·~18 min

What's changed: Initial version

6.1Python building blocks and interpreting a script

Key points

Treats Python's building blocks—variables, lists, dictionaries, loops, conditionals, and functions—not as syntax to memorize but as tools for reading what the script in front of you does and where it is wrong. You will learn to diagnose the network-automation staple of "iterate a device inventory and act only on matching devices" through its classic breaking points: indentation, missing keys, and return values.

When Python appears in ENCOR's domain 6, what is expected is not implementation skill as a programmer but reading skill as an operator. The exam does not ask "what is a variable"; it presents a dozen-odd lines of a script and asks you to predict its output or point out why it does not behave as intended. Automation scripts are remarkably formulaic: nearly all share the skeleton "iterate a device inventory (a list), inspect each device's attributes (a dictionary), and act only on those matching a condition." Once you hold that skeleton, and build the habit of checking three breaking points—whether the indentation places a line inside or outside the loop, whether the dictionary really has that key, and whether the function returns a value—you can reason about a script you have never seen.

6.1.1Elements that hold data: variables, lists, dictionaries

  • A list ([]) is an ordered, variable-length sequence, used to enumerate items of the same kind, as in devices = ["r1", "r2", "r3"]. Elements are indexed starting at 0 (devices[0] is "r1"), so reasoning "there are three, therefore devices[3]" raises an out-of-range error. In network automation, device inventories, interface lists, and result sets are lists.
  • A dictionary ({}) maps keys to values, bundling one device's attributes as in dev = {"host": "r1", "ip": "10.0.0.1"}. You read it with dev["ip"], but looking up a missing key with square brackets raises KeyError and halts the script. To avoid halting, the idioms are dev.get("ip") (yielding None when absent) or an existence check with if "ip" in dev:—a difference that matters with real inventories where fields are present for some devices and not others.
  • The practical point is that lists and dictionaries nest: a "list of dictionaries" such as inventory = [{"host": "r1", "role": "core"}, {"host": "r2", "role": "access"}] is the standard shape of API responses and inventories. With that shape, for dev in inventory: takes one device at a time and dev["role"] tests an attribute—so do not misread the correspondence: the outer list is "how many devices," the inner dictionary is "one device's attributes."

6.1.2Elements that control flow: loops, conditionals, indentation

  • A loop (for dev in inventory:) processes one element of a sequence at a time. In Python, indentation literally is the block boundary, playing the role braces play elsewhere. Consequently one level of indentation decides whether a line is inside or outside the loop, changing its execution count from "once per device" to "once total"—a summary print inside the loop fires per device, while outside it fires once at the end.
  • A conditional (if / elif / else) branches on a test. The classic mistakes here are confusing = (assignment) with (comparison) and case mismatches ("Core" "core" is false). Also, continue skips only the current iteration and moves to the next element, whereas break terminates the loop entirely—so writing break when you meant "skip non-matching devices" ends all processing at the first non-match.
  • A function (def check(dev):) names a routine for reuse. If you need a value back you must write return; a function without return yields None—so if you wrote result = check(dev) but the function only prints, result is None and any later if result: is always false. "It appeared on screen" does not mean "a value was returned"—the most-missed point in script diagnosis.
Exam point

ENCOR's Python items are fundamentally predicting a presented script's behavior and identifying its defect. Fix your reading order: (1) the data shape (list vs. dictionary, nested?) -> (2) the loop's extent (what indentation puts inside) -> (3) the test expression ( vs. =, letter case) -> (4) whether the function has a return. Those four almost always explain whether output is "per device or once," "halts midway," or "always false."

Suppose the operations team says, "I wrote a script to build a backup target list limited to core switches, but it does not behave as expected," and shows you this code. After inventory = [{"host": "sw1", "role": "core"}, {"host": "sw2", "role": "access"}, {"host": "sw3", "role": "core"}], they prepare targets = [], define def is_core(dev): whose body is only if dev["role"] "core": print(dev["host"]), call it with for dev in inventory: if is_core(dev): targets.append(dev["host"]), and finish with print(len(targets)). The symptom: running it prints sw1 and sw3 on screen, yet the final count shows 0. The biggest pitfall here is concluding "it printed, so the function judges correctly"—is_core has no return. The function does print, but it always returns None to its caller, None evaluates as false, so targets.append(...) never executes, and len(targets) is therefore 0. The fix is to change the print into return True (and return False when not matching), making the principle that "displaying" and "returning" are different acts the very key to the diagnosis. If the person instead suspected "maybe inventory is written wrong" and changed for dev in inventory: to for dev in inventory[0]:, they would iterate the first device's dictionary rather than the outer list, so dev would hold key strings ("host", "role") and dev["role"] would raise TypeError—a misreading of the structure in which the outer level is a list and the inner one a dictionary. As another common symptom, indenting print(len(targets)) inside the loop prints the count once per device (three times) instead of once at the end. When you see a "wrong number of times" symptom rather than an error, the standard move is to suspect the indentation level first. And writing the test as if dev["role"] = "core": fails to start at all with a syntax error, while if dev["Role"] "core": halts on the very first device with a KeyError—the goal of this section is to work backward from the symptom (nothing runs / halts midway / count is off / always empty) to the breaking point.

SymptomBreaking point to suspectFirst checkFix
Prints on screen but the count is 0The function lacks `return` and yields `None`Is there a `return` inside the `def`?Change `print` into `return True`/`return False`
Halts on the first deviceSquare-bracket lookup of a missing key raises `KeyError`Key spelling and letter caseUse `dev.get("ip")` or guard with `if "ip" in dev:`
Output repeats once per deviceA line meant to be outside the loop is indented inside itThe indentation level of that lineDedent one level to move it outside the loop
Devices after the first are not processed`break` was used where "skip this element" was intended`break` vs. `continue`Replace it with `continue`
Does not run at allA syntax error such as writing assignment `=` for comparison `==`The comparison operator on the `if` lineCorrect it to `==`
Warning

Trap: "The function prints, so the caller's if will be true" is wrong—a function without return yields None (false), so the later test fails even though output appeared. Also wrong: "indentation is decoration for readability"—in Python indentation defines the block itself, and one level changes the execution count. And "there are three devices, so devices[3]" is wrong—indexing starts at 0, so the third device is devices[2].

Variables/lists/dictionaries, and loops/conditionals/indentation.
What the script does and where it breaks

6.1.3Section summary

  • The standard shape of an automation script is iterating a "list of dictionaries" with for and filtering with if; the starting point is not misreading outer = list of devices, inner = dictionary of attributes
  • Script defects cluster into three points—indentation (loop extent), key existence (KeyError), and presence of return (None)—and you can work backward from the symptom to the breaking point
  • "Displaying" and "returning" are different: print gives you diagnostic material but never makes the caller's test true

Sign in to track progress — Log in.

Quick check

(just a quick review)

Q1. In a script that extracts only core switches, the function `def is_core(dev):` has a body consisting solely of `if dev["role"] == "core": print(dev["host"])`, and the caller is `for dev in inventory: if is_core(dev): targets.append(dev["host"])`. Running it prints the matching hostnames on screen, yet the final `print(len(targets))` is always 0. What is the most likely cause?

Q2. Intending to skip unsupported devices while scanning an inventory, you wrote `if dev["os"] != "IOS-XE": break` inside `for dev in inventory:`. If the second element of the inventory is not `IOS-XE`, what is the script's actual behavior?

Q3. While extracting the management IP from each dictionary in an inventory, only some devices lack the `mgmt_ip` key. The code currently reads `ip = dev["mgmt_ip"]`, and the script halts the moment it reaches a device without the key. Which fix best allows processing of the remaining devices to continue?

Check your understandingPractice questions for Chapter 6: Automation and Artificial Intelligence

Keep track of your progress

The full study guide is free to read. Sign up free to practice with the question bank, track what you have read, review your mistakes, and highlight passages.