code: def _construct_graph_from_dict_dict(
      cls,
      edges,
      directed: bool = False,
      vertex_name_attr: str = "name",
  ):
      """Constructs a graph from a dict-of-dicts representation.
  
      Each key can be an integer or a string and represent a vertex. Each value
      is a dict representing edges (outgoing if the graph is directed) from that
      vertex. Each dict key is an integer/string for a target vertex, such that
      an edge will be created between those two vertices. Integers are
      interpreted as vertex_ids from 0 (as used in igraph), strings are
      interpreted as vertex names, in which case vertices are given separate
      numeric ids. Each value is a dictionary of edge attributes for that edge.
  
      Example:
  
        >>> {'Alice': {'Bob': {'weight': 1.5}, 'David': {'weight': 2}}}
  
      creates a graph with three vertices (Alice, Bob, and David) and two edges:
  
        - Alice - Bob (with weight 1.5)
        - Alice - David (with weight 2)
  
      @param edges: the dict of dict of dicts specifying the edges and their
        attributes
      @param directed: whether to create a directed graph
      @param vertex_name_attr: vertex attribute that will store the names
  
      @returns: a Graph object
      """
      first_item = next(iter(edges), 0)
  
      if not isinstance(first_item, (int, str)):
          raise ValueError("Keys must be integers or strings")
  
      vertex_attributes = {}
      edge_attribute_list = []
      if isinstance(first_item, str):
          name_map = UniqueIdGenerator()
          edge_list = []
          for source, target_dict in edges.items():
              source_id = name_map[source]
              for target, edge_attrs in target_dict.items():
                  edge_list.append((source_id, name_map[target]))
                  edge_attribute_list.append(edge_attrs)
          vertex_attributes[vertex_name_attr] = name_map.values()
          n = len(name_map)
  
      else:
          edge_list = []
          n = -1
          for source, target_dict in edges.items():
              n = max(n, source, *target_dict)
              for target, edge_attrs in target_dict.items():
                  edge_list.append((source, target))
                  edge_attribute_list.append(edge_attrs)
          n += 1
  
      # Construct graph without edge attributes
      graph = cls(n, edge_list, directed, {}, vertex_attributes, {})
  
      # Add edge attributes
      for edge, edge_attrs in zip(graph.es, edge_attribute_list):
          for key, val in edge_attrs.items():
              edge[key] = val
  
      return graph
  
file_path: python-igraph/src/igraph/io/objects.py
imports: from typing import Union, Sequence
  from collections import defaultdict
  from itertools import repeat
  from warnings import warn
  from igraph.datatypes import UniqueIdGenerator
label_desc: Use image node_id 4 for calling a global function with example usage: _construct_graph_from_dict_dict(cls, edges, directed, vertex_name_attr) and returns: graph