用于创建字典的函数中的默认参数会创建一个无限循环的自引用

default argument in function for creating a dictionary creates an infinete loop of self references

a 有以下代码的问题:

from lxml import etree as ET

class test(object):
    def __init__(self, **args):
        self.tag = []
        self.meshfiles = []
        self.root = None
    def addMesh(self, **args):
        if not "filename" in args:
            raise KeyError("No filename given")
        else:
            self.meshfiles.append(args["filename"])
    def populateTree(self, tag, text='', attr={}, children={}):
        return {'tag': tag, 'text': text, 'attr': attr, 'children': children}

    @property
    def tree(self):
        baum = {'meshes': {}}
        if len(self.meshfiles) == 1:
            baum['meshes'] = self.populateTree('mesh', text=self.meshfiles[0])
        else:
            baum['meshes'] = self.populateTree('meshes')
            for i, meshfile in enumerate(self.meshfiles):
                # works:
                baum['meshes']['children'][i] = self.populateTree('mesh', text=meshfile)
                # not working:
                # baum['meshes']['children'][i] = self.populateTree('mesh', text=meshfile, children={})
        return baum

    def createXML(self):
        self.root = ET.Element("rootelement")
        self.dict2xml(self.root, self.tree)
        return ET.tostring(self.root, pretty_print=True)
    def dict2xml(self, parent, dictionary):
        for entry in dictionary:
            self.tag.append(ET.SubElement(parent, dictionary[entry]['tag']))
            self.tag[-1].text = str(dictionary[entry]['text'])
            for attr in dictionary[entry]['attr']:
                self.tag[-1].set(attr, dictionary[entry]['attr'][attr])
            if len(dictionary[entry]['children']) > 0:
                self.dict2xml(self.tag[-1], dictionary[entry]['children'])
if __name__ == "__main__":
    t = test()
    t.addMesh(filename="a")
    t.addMesh(filename="c")
    t.addMesh(filename="b")
    print(t.tree)
    print(t.createXML())

虽然这个例子给出了递归错误:

    {'meshes': {'tag': 'meshes', 'text': '', 'attr': {}, 'children': {0: {'tag': 'mesh', 'text': 'a', 'attr': {}, 'children': {...}}, 1: {'tag': 'mesh', 'text': 'c', 'attr': {}, 'children': {...}}, 2: {'tag': 'mesh', 'text': 'b', 'attr': {}, 'children': {...}}}}}
Traceback (most recent call last):
  File "/home/buchwalj/temp/bug-python/test.py", line 48, in <module>
    print(t.createXML())
  File "/home/buchwalj/temp/bug-python/test.py", line 32, in createXML
    self.dict2xml(self.root, self.tree)
  File "/home/buchwalj/temp/bug-python/test.py", line 41, in dict2xml
    self.dict2xml(self.tag[-1], dictionary[entry]['children'])
  File "/home/buchwalj/temp/bug-python/test.py", line 41, in dict2xml
    self.dict2xml(self.tag[-1], dictionary[entry]['children'])
  File "/home/buchwalj/temp/bug-python/test.py", line 41, in dict2xml
    self.dict2xml(self.tag[-1], dictionary[entry]['children'])
  [Previous line repeated 994 more times]
  File "/home/buchwalj/temp/bug-python/test.py", line 36, in dict2xml
    self.tag.append(ET.SubElement(parent, dictionary[entry]['tag']))
 RecursionError: maximum recursion depth exceeded while calling a Python object

注释掉第 27 行而不是第 25 行会打印出具有相应 xml 的正确字典。唯一的区别是,工作示例使用与函数调用中的默认参数相同的参数,而不是默认参数本身。 这是一个错误还是我在这里做错了什么?

使用空字典作为默认值实际上是个坏主意,主要是因为字典是可变对象,这意味着如果您不小心更改了字典的内容,它会保留在那里(因为对对象的引用保持不变)。参见:Why is the empty dictionary a dangerous default value in Python?

我建议将您的 populateTree 函数更改为:

def populateTree(self, tag, text='', attr=None, children=None):
        if attr is None:
            attr = {}
        if children is None:
            children = {}
        return {'tag': tag, 'text': text, 'attr': attr, 'children': children}

因此它创建了一个新的空字典,所有 populateTree 都在没有这些参数的情况下被调用。