Sort edges, greedily accept those that don't form a cycle.
def kruskal(graph):
mst = []
edges = sorted(graph.edges, key=lambda e: e.w)
uf = UnionFind(graph.nodes)
for u, v, w in edges:
if uf.union(u, v): # No cycle -> Accept
mst.append((u, v, w))
Dynamically typed and interpreted — every comparison and swap is dispatched by the interpreter at run time, so tight loops run roughly 10–100× slower than compiled C/C++. Unbeatable for learning the idea with the least code; not what you reach for when the inner loop is the bottleneck.