Search code examples
pythonnetmiko

Python updating dictionary and appending at the end of list with loop


Is there a proper way of updating a dictionary and appending that dictionary to a list without causing duplicates. This is for use with netmiko and my target is to make it a little bit dynamic and flexible especially with the naming scheme.

#!/usr/bin/python
import re

hosts_info = []
host = {
    'device_type': 'cisco_ios',
    'ip': 'co-acc-sw',
    'username': 'cisco',
    'password': 'notapassword',
    'secret': 'mrsecret'
}

for x in range(0, 4):
    if x < 2:
        host.update({"ip": "core-switch" + str(x)})
    elif x > 2:
        host.update({"ip": "access-switch" + str(x - 1)})
    hosts_info.append(host)
print(hosts_info)

Current output is:

[{'device_type': 'cisco_ios', 'ip': 'access-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'access-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'access-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'access-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}]

I am trying to get it to look like this:

[{'device_type': 'cisco_ios', 'ip': 'core-switch1', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'core-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'access-switch1', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}, 
{'device_type': 'cisco_ios', 'ip': 'access-switch2', 'username': 'cisco', 'password': 'notapassword', 'secret': 'mrsecret'}]

I can get it to work if I create separate dictionaries and add them to separate if statements just wondering if there is a better way to do this? thanks.


Solution

  • Each item in the list needs to be its own dict; your existing code just has a single dict (host) that it keeps updating (overwriting previous updates) and builds a list with four references to the same dict.

    An easy way to create a new dict that's a partial copy of an existing dict is to do a dictionary comprehension with **olddict:

    for x in range(0, 4):
        if x < 2:
            hosts_info.append({**host, "ip": "core-switch" + str(x)})
        elif x > 2:
            hosts_info.append({**host, "ip": "access-switch" + str(x - 1)})
        else:
            hosts_info.append({**host})
    

    This is more what your original code is trying to do (minus the pointer errors), but still doesn't actually produce the output you want because the math is wrong.

    I think the way to make the math work would be something more like:

    hosts_info = [{
        **host, "ip": f"core-switch{x+1}" if x < 2 else f"access-switch{x-1}"
    } for x in range(4)]
    

    Or instead of trying to cleverly engineer a single loop that produces the different string values you want by computing offsets from the different int values in a range, you could just do a nested loop where you iterate over the actual values you want:

    hosts_info = [{
        **host, "ip": f"{switch}-switch{x}"
    } for switch in ("core", "access") for x in (1, 2)]
    

    This is by far more readable IMO, since the output corresponds very directly and obviously to the code, whereas the tricky math makes it hard to figure out what the original code is even attempting to produce (and it's nontrivial to get the math to do the right thing).