Python3: Constructing an "instancemethod" from a string


#1

I’m working on porting my main project from Python 2 to Python 3. For the most part things have gone reasonably well: Once I found out how to direct it to all of my Python files in a single command, “2to3” did the first pass at conversion. (And even seems to have saved backups!) Strings look like they’re more of a pain now, but there seem to be solutions that are simple enough.

However, I’ve hit a problem that I don’t know what to do about, and I don’t seem to be finding solutions online. (Although I may well have missed something.)

My problem is this:

In certain cases, I want to be able to save and restore references to methods–such as when saving and restoring a mapping from key-press to key-event.

Under Python 2 this was achieved by saving the string “instancemethod” as the “type” of the object being saved, and the method’s name as the “value” of the object. When loaded, the game-saving module would then, as per usual, employ “eval” to construct the desired object from its type-string and value–in this case ending up with “eval(‘instancemethod’+(methodName)”. This produced the desired method, and allowed me to restore such method-references.

However, under Python 3 I get the error “NameError: name ‘instancemethod’ is not defined”. :/

Does anyone here know how to achieve the same thing under Python 3 as I was under Python 2?


#2

I’m not sure what instancemethod was doing, so to verify what you’re asking for, say we have this code:

class SomeClass:
    def some_method(self, some_value):
        print('doing something with', some_value)

foo = SomeClass()
save_this = foo.some_method

and you want some way to get as a string "foo.some_method" from the variable (name?) save_this ?

Getting the name of the method and the name of the class is simple, just use save_this.__name__ and/or save_this.__qualname__ but as far as I know getting the name(-s) of the instance - that’s going on an adventure looking in the interpreter stack.


#3

Try using types.MethodType(name, self) to replace instancemethod (disclaimer, I haven’t tested it :slight_smile: )


#4

The problem isn’t there. (Or rather, wasn’t–see below…)

The basic idea is that I already have a reference to an “instancemethod” saved. Looking at my code (it’s been a while since I worked on this side of the project), I imagine that “instancemethod” was the name given by Python for the class of such a reference–that is, the result of “obj.__class__.__name__” where “obj” was a reference to a method.

In Python 2, I could restore this saved data like so: “instancemethod([name-of-method])”, or so it seems. (I’m… not sure of where it was getting the reference to the object to which the method belongs… o_0 As I said it’s been a while.)

In Python 3, this produces an error.

The weird thing is that I already have that elsewhere. But perhaps this reflects a change between Python 2 and 3–perhaps Python 2 had both “instancemethod” and “types.MethodType”, but Python 3 doesn’t? I don’t know.

However! Having now looked more closely in working on this response, it seems that I may be using this feature only in my key-bindings. For one, those I can re-generate easily, meaning that a change might not break things like my level-files. For another, it looks like I might be saving more than is required in this case–all that’s really called for is the control to which an input has been bound, and what that input was. The callback shouldn’t change, and thus can be omitted from saving and loading.

So, for now, I’m going to cautiously say that I may get away with just changing how I handle my key-binding saves, and hope that it doesn’t become a problem later… If it does, I can revisit this.

Thank you both for your replies! :slight_smile:


#5

I am unfamiliar with instancemethod([name-of-method]); I don’t have instancemethod defined in my Python 2 installations.

If you are trying to get a reference to a method that belongs to an object by name, should you not be using getattr(), or accessing the class’ __dict__ directly? Having some more context or some of your code that uses it might help us come up with a cleaner solution that does not involve eval().


#6

I’m afraid that “eval” is used either way: it’s pretty much how my generalised saving-and-loading module works. (In short, the module for the most part doesn’t need to know too much about the specific game in which it’s used; roughly-spaking, it’s just given a type-string, and attempts to construct it.) This is my old “GameSaver” module, which I posted a few years ago, I think; as I recall, there was discussion back then about the safety of using “eval”.

(I suppose that I could require the game to register every possible class that might be saved or loaded, and have it reconstruct based on that, rather than “eval”. It might be worth thinking about–although it had its own drawbacks, I fear.)

As I said, however, it looks like I’m now fine without “instancemethod”:

I found that it seemed to be used in only one place, and was in fact redundant there. I’ve thus simplified the data that’s saved, and thus loaded, in that particular class. Furthermore, since it looks like “instancemethod” was coming up as a result of being reported as a class-name (or similar), if it’s not present in Python3, it likely won’t come up in future saves.