Distributed (asynchronous) averaging
The way that the random graph is generated seems suspicious:


class Network(object):
    def __init__(self, num_nodes):
        self.nodes = [0]*num_nodes
        for i in range(num_nodes):
            self.nodes[i] = Node(randint(1, 10))
        self.set_connections()

    def set_connections(self):
        for f, g in product(self.nodes, self.nodes):
            p = uniform()
            if p &lt; 0.8:
                f.friends.append(g)
                g.friends.append(f)



First of all, self.nodes should just be defined as

self.nodes = [Node(randint(1, 10)) for _ in range(num_nodes)]


The way that nodes changes type from a list of integers to a list of Node objects makes it feel slightly more dirty.

It looks like set_connections() is only meant to be called from the constructor, so it should be renamed _set_connections() to suggest that it is private.  However, considering that __init__() would just consist of two lines (self.nodes = … and self._set_connections()), I would just lump everything into __init__().

product(self.nodes, self.nodes) seems wrong.  It's possible to cause a Node to befriend itself.  It's also possible to have two nodes list each other as friends twice.  I suspect that what you want is combinations(self.nodes, 2).

def __init__(self, num_nodes):
    self.nodes = [Node(randint(1, 10)) for _ in range(num_nodes)]
    for f, g in combinations(self.nodes, 2):
        if uniform() &lt; 0.8:
            f.friends.append(g)
            g.friends.append(f)