调用对象时如何解决“ RecursionError:超出最大递归深度”?

时间:2019-04-27 08:54:50

标签: python python-3.x

我正在尝试将类的实例保存到Python 3.7中的字符串中。

我正在尝试这样做是因为我已经开始制作一个基于文本的游戏,该游戏具有世界坐标系和类实例的字典(我遵循了本教程:https://letstalkdata.com/2014/08/how-to-write-a-text-adventure-in-python/),我想要添加一个保存选项。如果您只是腌制世界词典,它会说房间在RAM中的什么位置,这不是很有帮助。但是后来我在某个地方读到了repr()exec(),我现在正试图用它来保存和加载测试类的实例。但是,当我尝试打印新创建的类实例时,它会给我一个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))

我希望输出为:

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!')

但是我得到了

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

我该如何解决?

2 个答案:

答案 0 :(得分:1)

对象的名称是对象本身。如果您打印执行的内容,则它看起来像这样:

global i
i = Class(1, 'str', 'Hello', 'world!')
global i
i.name = i

在i上调用repr时,它将尝试将i.name表示为字符串,但要转换i.name(即i)以转换它调用的ta字符串reprrepr然后将尝试将i.name.name(即i.name,即i表示为字符串,...直到超过递归深度为止。

在类加载中,您宁愿想要

i.name = 'i'

一般来说,使用repr和exec作为保存系统不是一个好主意。这是非常挑剔的,基本上可以归结为repr不被设计为机器可读(但人类可读),而exec几乎从来不是任何事物的正确选择。相反,您可以使用de / serializer(即将python对象转换为字节并将字节转换为python对象的代码),例如pickle或json或protobuf或xml。我真的不理解您对泡菜的反对,因为它基本上是为您的用例设计的。

答案 1 :(得分:0)

关于您对泡菜的评论-在文档(https://docs.python.org/3.7/library/pickle.html#comparison-with-marshal)中说:

  

pickle模块跟踪已序列化的对象,因此以后对同一对象的引用将不再被序列化。

大概是为什么有内存引用。只需执行以下操作就很容易了:

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', '!']

注意,尽管如此,我也删除了代码中的类名,这就是为什么打印输出与众不同的原因。