useful_functions.py
 1  #! /usr/bin/env python3
 2  # -*- coding: utf-8 -*-
 3  
 4  """A non-thematic collection of useful functions."""
 5  
 6  
 7  def recursively_replace(original, replacements, include_original_keys=False):
 8      """Clones an iterable and recursively replaces specific values."""
 9      
10      # If this function would be called recursively, the parameters 'replacements' and 'include_original_keys' would have to be 
11      # passed each time. Therefore, a helper function with a reduced parameter list is used for the recursion, which nevertheless
12      # can access the said parameters.
13      
14      def _recursion_helper(obj):
15          #Determine if the object should be replaced. If it is not hashable, the search will throw a TypeError.
16          try:
17              if obj in replacements:
18                  return replacements[obj]
19          except TypeError:
20              pass
21  
22          # An iterable is recursively processed depending on its class.
23          if hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
24              if isinstance(obj, dict):
25                  contents = {}
26  
27                  for key, val in obj.items():
28                      new_key = _recursion_helper(key) if include_original_keys else key
29                      new_val = _recursion_helper(val)
30  
31                      contents[new_key] = new_val
32  
33              else:
34                  contents = []
35  
36                  for element in obj:
37                      new_element = _recursion_helper(element)
38  
39                      contents.append(new_element)
40  
41              # Use the same class as the original.
42              return obj.__class__(contents)
43  
44          # If it is not replaced and it is not an iterable, return it.
45          return obj
46  
47      return _recursion_helper(original)