Reputation: 229
I'm trying to save instances of classes to strings in Python 3.7.
I'm trying this because I've started to make a text-based game that has a dictionary of co-ordinates and instances of classes for the world (I followed this tutorial: https://letstalkdata.com/2014/08/how-to-write-a-text-adventure-in-python/) and I want to add a save option. If you just pickle the world dictionary it said where in the RAM the rooms were, which wasn't very helpful. But then I read about repr()
and exec()
somewhere and I am now trying to use it to save and load instances of a test class. But when I try to print my newly created instance of the class it gives me a RecursionError: maximum recursion depth exceeded
.
import traceback
def change_char(s, p, r):
l = list(s)
l[p] = r
return "".join(l)
class Class:
def __init__(self, i1, i2, *i3):
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
self.name = text[:text.find('=')].strip()
self.i1 = i1
self.i2 = i2
self.i3 = []
for iv in i3:
self.i3.append(iv)
def l(self, l):
s = ''
i = 1
for e in l:
if i < len(l):
s = s + repr(e) + ", "
else:
s = s + repr(e)
i += 1
return s
@property
def print_vals(self):
print('i1 : {1}\ni2 : {2}\ni3 : {3}'.format(self.i1, self.i2, self.l(self.i3)))
def __repr__(self):
return '{0} = Class({1}, {2}, {3})'.format(self.name, repr(self.i1), repr(self.i2), self.l(self.i3))
@property
def save(self):
return repr(self)
def Classload(st):
name = st[:st.find('=')].strip()
exec('global '+name+'\n'+st)
exec('global '+name+'\n'+name+'.name = '+name)
c = Class(1, "str", "Hello", 'world!')
print(repr(c))
i = c.save
i = change_char(i, 0, 'i')
print(i)
Classload(i)
print(c)
print(i)
print(repr(c))
print(repr(i))
I expect the output to be:
c = Class(1, 'str', 'Hello', 'world!')
i = Class(1, 'str', 'Hello', 'world!')
c = Class(1, 'str', 'Hello', 'world!')
i = Class(1, 'str', 'Hello', 'world!')
c = Class(1, 'str', 'Hello', 'world!')
i = Class(1, 'str', 'Hello', 'world!')
But I get:
c = Class(1, 'str', 'Hello', 'world!')
i = Class(1, 'str', 'Hello', 'world!')
c = Class(1, 'str', 'Hello', 'world!')
Traceback (most recent call last):
File "C:\Users\HP\Desktop\test.py", line 107, in <module>
print(i)
File "C:\Users\HP\Desktop\test.py", line 63, in __repr__
return '{0} = Class({1}, {2}, {3})'.format(self.name, repr(self.i1), repr(self.i2), self.l(self.i3))
File "C:\Users\HP\Desktop\test.py", line 63, in __repr__
return '{0} = Class({1}, {2}, {3})'.format(self.name, repr(self.i1), repr(self.i2), self.l(self.i3))
File "C:\Users\HP\Desktop\test.py", line 63, in __repr__
return '{0} = Class({1}, {2}, {3})'.format(self.name, repr(self.i1), repr(self.i2), self.l(self.i3))
[Previous line repeated 245 more times]
RecursionError: maximum recursion depth exceeded
How do I fix this?
Upvotes: 0
Views: 5453
Reputation: 647
With regard to your comments about pickle - in the docs (https://docs.python.org/3.7/library/pickle.html#comparison-with-marshal) it says:
The pickle module keeps track of the objects it has already serialized, so that later references to the same object won’t be serialized again.
Presumably why there are the memory references. It looks a lot easy to just do the following:
if __name__ == '__main__':
import pickle
initial = Class(1, 'hello', 'world', '!')
dumped = pickle.dumps(initial)
returned = pickle.loads(dumped)
print(f'initial: {initial}')
print(f'returned: {returned}')
print(f'i1: {returned.i1}, i2: {returned.i2}, i3: {returned.i3}')
# initial: Class(1, 'hello', 'world', '!')
# returned: Class(1, 'hello', 'world', '!')
# i1: 1, i2: hello, i3: ['world', '!']
Note, I had also removed the bits in your code for the class name though, which is why the printout is a touch different.
Upvotes: 0
Reputation: 7384
The name of your object is the object itself. If you print what you execute it looks something like this:
global i
i = Class(1, 'str', 'Hello', 'world!')
global i
i.name = i
When you call repr
on i it will try to represent i.name
as a string, but to convert i.name
(which is i
) to convert t a string it calls repr
on it. repr
will then try to represent i.name.name
(which is i.name
which is i
) as a string, ... until you exceed the recursion depth.
In your classload you rather want something like
i.name = 'i'
On a more general note using repr and exec as a save system is not a good idea. It is very finicky which basically boils down to repr not being designed as machine readable (but human readable) and exec being almost never the right choice for anything. Instead you can use de/serializer (i.e. code that translates python objects to bytes and bytes to python objects) like pickle or json or protobuf or xml. I don't really understand your objection to pickle, because it is basically designed for your usecase.
Upvotes: 2