After much searching, the only way I have found to solve my particular problem is to use dynamic inheritance. It is easy enough following the guide from here and a few other SO questions; most table is this.
Using a modified version of the contrived example from the first link:
def makeinst(cls, *args, **kwargs):
class NewClass(cls): pass
return NewClass(*args, **kwargs)
mylist = makeinst(list,(1,2))
This works as I would hope but it can't be pickled:
pickle.dumps(mylist)
...
AttributeError: Can't pickle local object 'makeinst.<locals>.NewClass'
I understand why this doesn't work but what I want to know is there a way around it? Is there a better way to dynamically subclass something?
(FWIW, dill
can't do it either. See dill issue #56)
You can create the class in the global
scope of the module, to do that you need to create a class manually with the type(name, bases, class_dict)
call
import pickle
def makeinst(name, cls, *args, **kwargs):
# This will be a method
def foo(self):
return f"I am: {self!r}"
globals()[name] = type(
name,
# This must be a tuple
# (cls) evaluate to cls
# (cls,) evaluates to a tuple containing cls as its only element
(cls,),
# Methods, classmethods, staticmethods and all class-level data
{
"foo": foo
},
)
return globals()[name](*args, **kwargs)
my_list = makeinst("MyList", list, [1, 2, 3])
print(my_list) # [1, 2, 3]
data = pickle.dumps(my_list)
my_list_unpickled = pickle.loads(data)
print(my_list_unpickled) # [1, 2, 3]
print(my_list_unpickled.foo()) # I am: [1, 2, 3]
In another program execution you must call makeinst("MyList", list)
at least once before unpickling to define the class.