属性错误:无法为Python列表属性设置属性。

4

我正在使用从一个分支版本中获取的python-docx库进行编程,但是在编辑元素列表时遇到了问题,因为它被定义为属性。

# docx.document.Document
@property
def elements(self):
    return self._body.elements

我尝试使用这里提到的解决方案,但是错误AtributeError: can't set attribute仍然出现。
接下来我尝试将setter添加到从self._body派生的属性中,并编辑代码:

# docx.blkcntnr.BlockItemContainer

@property
def elements(self):
    """
    A list containing the elements in this container (paragraph and tables), in document order.
    """
    return [element(item,self.part) for item in self._element.getchildren()]

我尝试在两个级别中都添加了setter,但最终又遇到了错误AtributeError: 无法设置属性

我编写的setter

@elements.setter
def elements(self, value):
    return value 

我尝试的实现:

elements_list = docx__document.elements
elem_list = []
docx__document.elements = elements_list = elem_list

这段代码的主要问题是docx__document.elements仍然包含了所有应该被删除的元素!

编辑库的过程如下:

# Inside docx.document.Document
@property
def elements(self):
    return self._body.elements

@elements.setter
def elements(self, value=None):
    self._body.elements = value
    gc.collect()
    return value

另一部分:

# Inside docx.blkcntnr.BlockItemContainer

@property
def elements(self):
    """
    A list containing the elements in this container (paragraph and tables), in document order.
    """
    return [element(item,self.part) for item in self._element.getchildren()]


@elements.setter
def elements(self, value):
    """
    A list containing the elements in this container (paragraph and tables), in document order.
    """
    return value

相关问题 [更新]


如果我为这个属性添加了一个setter:
# docx.document.Document

@property
def elements(self):
    return self._body.elements

我应该为这个 property 添加一个 setter 吗:

# docx.blkcntnr.BlockItemContainer

@property
def elements(self):
    """
    A list containing the elements in this container (paragraph and tables), in document order.
    """
    return [element(item,self.part) for item in self._element.getchildren()]

因为document.elements的值实际上是来自于document._body.elements,我说得对吗?

非常感谢任何帮助!


嗨@Jasmijn,代码和错误已经附上,同时也列出了我在解决这个问题时所采取的步骤。 - Ahmad
1
感谢您编辑您的问题。不幸的是,它仍然无法重现 - Jasmijn
嗨,@Jasmijn,你能再次检查一下这个问题吗?如果好的话,你可以给它点赞吗? - Ahmad
1
我不熟悉这个库,但是我觉得你的方法有误。elements getter 返回一个由其子元素组成的对象列表解析(return [element(..) for item in self._element.getchildren()])。该 getter 总是从其子元素创建一个新列表,因此将属性“element”本身设置为空列表不能删除子元素。建议查看实例的 _element 属性,可能要检查 def getchildren() 带你去哪儿。看看用于 创建 列表的内容,而不是列表本身。 - micromoses
2个回答

2

首先提醒一句:如果您编辑一个库,那么在升级该库时,这些更改将被抹掉。

尽管您的问题仍无法重现,但我现在可以看出问题所在了。

设置器需要改变某种状态才能有用。它们不返回值。

# Inside docx.document.Document

@elements.setter
def elements(self, value):
    self._body.elements = value


# Inside docx.blkcntnr.BlockItemContainer

@elements.setter
def elements(self, value):
    # FIXME

很遗憾,我还没有找到如何以整洁的方式替换XML树来实现docx.blkcntnr.BlockItemContainer.elements.setter的方法。

既然看起来你想要清除文档,为什么不直接实例化一个新的Document呢?像这样:

docx__document = docx.Document()

使用 docx__document = docx.Document() 仍会在内存中创建一个对象,这与我的目标相反。我试图清除所有内容以节省内存空间。 - Ahmad
警告:如果您编辑一个库,那些更改将在升级库时被清除。 我知道这一点,我计划将更新推送到它的存储库。
- Ahmad
嗨@Jasmijn,我已更新了问题,如果您能帮忙处理最后的更新,我将不胜感激,并且可以批准答案。谢谢! - Ahmad
1
很遗憾,根据您提供的信息,我已经给出了我能够提供的最佳答案。除非我能够重现您实际的问题,否则我没有其他更多的事情可做,只能进行猜测。而且我甚至不清楚您真正的问题是什么。为什么您需要回收内存? - Jasmijn
1
哦,答案是肯定的。 - Jasmijn
显示剩余3条评论

2
主要的“Attribute Error”问题,@Jasmijn已经解决了... setter实际上需要设置一些东西。
关于如何为elements提供setter:
首先,我们需要弄清楚elements来自哪里:
- Document.elements来自[Document]._body.elements - [Document]._body.elements来自_Body,它继承了BlockItemContainer.elements - BlockItemContainer.elements[BlockItemContainer]._element.getchildren()动态构建其元素列表。 - [BlockItemContainer]._element等于[Document]._element.body - [Document]._element来自扩展ElementProxy,是传递给Document构造函数的第一个参数
非常迂回地,考虑到传递给Documentelement,文档的elements源自:element.body.getchildren()。(跟踪查找链有点棘手,但这就是当有很多抽象或者可能存在较差的面向对象设计时所得到的结果)
现在跟踪一下getchildren()到底做了什么:
- 看起来传递给Documentelement来自包含的oxml包 - oxml本身是lxml的一个包装器 - 看起来相关的类实际上在Cython中。据我所知,_Element类是getchildren()最终定义的地方(etree.pyx) - getchildren()调用_collectChildren(apihelpers.pxi),这让你了解了内部元素结构的设置方式
鉴于根实现是Cython会使事情复杂化,但我看到_Element类实现了一些额外的方法,你可以利用它们,特别是:clear()extend()
因此,可能的实现(我已经测试过,似乎可以工作):
# inside docx.document.Document
@elements.setter
def elements(self, lst):
    cython_el = self._element.body
    cython_el.clear()
    cython_el.extend(lst)

我不同意@Jasmijn的看法,我认为你不需要为BlockItemContainer提供setter,因为那是一个私有类。

如果需要的话,你也可以直接在Document对象上公开其他_Element方法,比如clear()


很不错。在追踪代理对象到代理对象和包装库到包装库的过程中,我有些迷失了方向,所有这些似乎都没有得到很好的记录或完全没有记录。至于我们的分歧,那是公平的。我只考虑在文档上设置一个委托实际更改元素工作的setter方法self._element,这是我的首选。 - Jasmijn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接