Write (dictionaries which contain) function pointers to file and read it back

I was looking for a way to save a dictionary to file and read it back. I came across this solution: Writing a dict to txt file and reading it back?

My dictionary however contains function pointers (is it called this way in Python?). The concrete dictionary initialisation looks something like this:

dictionary = {
    'string': 'value',
    'integer': 42,
    'function': some_func
  }

with some_func being some global function:

def some_func ():
  print('This function has been called!')

The solution linked above breaks down in this case. To save the dictionary to file, it proposes to produce a string from the dictionary via str and store this string in a file. The string could then, as suggested, be read back again and restored to a dictionary via eval:

dictionary_string = str(dictionary) # String from dictionary
dictionary = eval(dictionary_string) # Restore the dictionary from string

However, Python inserts a type indicator in place of the function pointer in my example, which is of course syntactical nonsense to Python. My dictionary would as a string look like this:

{'string': 'value', 'integer': 42, 'function': <function some_func at 0x00000000002a>}

I’m not giving much on reading in the pointer, I can live without it. But I’d like to find a simple solution which can handle such cases. It can be either by reading in the pointer, even if it points to nowhere, or by just neglecting the type indicator <> (replacing it to None or whatever).

I also considered storing the function name as a string, but I refrained from handling it this way because Python doesn’t seem to have a beautiful solution to call a global function by a string. My hope was that there was maybe a solution to my problem that is just a bit less ugly than calling functions by global()[dictionary['function']]().

Answer

Here’s an example using JSON to serialize your dictionary, with the custom encoder handling the replacement of any function object with None:

import json
import types


class FunctionEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, types.FunctionType):
            return None
        return super().default(obj)


def some_func():
    pass


dictionary = {
    'string': 'value',
    'integer': 42,
    'function': some_func
    }

dict_json = json.dumps(dictionary, cls=FunctionEncoder)

new_dict = json.loads(dict_json)

print(new_dict)

When run, it outputs

{'string': 'value', 'integer': 42, 'function': None}

You could also serialize the function to its name instead,

class FunctionEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, types.FunctionType):
            return getattr(obj, '__name__', '<anonymous function>')
        return super().default(obj)

(I’m not sure the use of getattr is necessary; return obj.__name__ may work fine.)


A possibility is to define an encoder/decoder pair that serializes a function by encoding its byte code, signature, etc individually, then reconstructing an instance from those parts. If it’s possible, it’s not something I want to attempt here.