1

经过几年的中断后,我才重新开始编码,我正在尝试以一种让我在特定表单级别或整个子树上抓取和执行操作的方式对多层静态表单进行建模。

示例表单层次结构:

  • 我的表格
    • 问题 1
    • 第1部分
      • 问题 1.1
    • 第2部分
      • 问题 2.1
      • 第 1 部分
        • 问题 2.1.1
        • 问题 2.1.2
    • 问题2

每个问题将具有多个属性(问题文本、是否为必填字段等),并且问题可以位于层次结构的任何级别。

我希望能够做这样的事情:

>>> MyForm.getQuestionObjects()
[Question1, Question1_1, Question2_1, Question2_1_1, Question2_1_2, Question2]

>>> MyForm.Part2.getQuestionObjects()
[Question2_1, Question2_1_1, Question2_1_2]

和/或类似的东西:

>>> # Get questions (return class members)
>>> MyForm.SubPart1.getQuestions()
(('2.1.1 text', otherAttributes), ('2.1.2 text', otherAttributes))

>>> # Get questions -- but replace an attribute on 2.1.2
>>> MyForm.Part2.getQuestions(replace_attr('Question_2_1_2', 'text', 'New text'))
(('2.1.1 text', otherAttributes), ('New text', otherAttributes))

我一直在尝试使用嵌套/内部类来做到这一点,这是一个令人头疼的问题,并且在 python 中没有得到很好的支持。但即使我能找到使用嵌套类的解决方案,我仍然想知道是否有更好的方法将此表单信息存储在某个地方,以便非编码人员更容易编辑(可能是纯文本模板),然后加载运行时的数据,因为它是静态的,我经常需要它在内存中。表单数据的更新不会超过每月一次。无论我如何存储数据,我都想找出一个好的数据结构来表示、遍历和操作它。

  • 有没有办法制作这样的分层属性对象?
  • 我可以做类似多维命名元组的事情吗?
  • 还有其他想法吗?

感谢您的任何评论。

4

3 回答 3

2

我会将此类分层数据以 XML 格式存储在存储设备中。您可以使用xml.etree.ElementTree标准模块将此类 XML 文件加载到 Python 中的分层数据结构中,对其进行更改,然后将其保存回文件中。这样您就不必担心实际的数据结构,因为它是由 ElementTree 自动构建的。

请参阅 Python 手册中的 xml.etree.ElementTree。更多信息可以在这里找到:

(Python 中还有其他成熟的解决方案可以将 XML 文件加载到各种数据结构中。只需选择一个最容易用于您的任务的解决方案。Google 是您的朋友。:-))

于 2009-11-23T05:13:52.330 回答
2

Python 中的嵌套类没有什么令人头疼或不支持的地方,只是它们什么都不做。不要期望自动将 Java 内部类样式链接返回到所有者实例:嵌套类只不过是普通类,其类对象恰好存储在另一个类的属性中。他们在这里帮不了你。

有没有办法制作这样的分层属性对象?

当然可以,但是您最好扩展 Python 现有的序列类,以获得所有现有操作的好处。例如,表单 'part' 可能只是一个也有标题的列表:

class FormPart(list):
    def __init__(self, title, *args):
        list.__init__(self, *args)
        self.title= title
    def __repr__(self):
        return 'FormPart(%r, %s)' % (self.title, list.__repr__(self))

现在您可以form= FormPart('My form', [question, formpart...])使用普通的列表索引和切片来说出和访问其中的问题和表单部分。

接下来,问题可能是像元组这样的不可变事物,但也许您希望其中的项目具有漂亮的属性名称。所以添加到tuple

class FormQuestion(tuple):
    def __new__(cls, title, details= '', answers= ()):
        return tuple.__new__(cls, (title, details, answers))
    def __repr__(self):
        return 'FormQuestion%s' % tuple.__repr__(self)

    title= property(operator.itemgetter(0))
    details= property(operator.itemgetter(1))
    answers= property(operator.itemgetter(2))

现在您可以定义您的数据,例如:

form= FormPart('MyForm', [
    FormQuestion('Question 1', 'Why?', ('Because', 'Why not?')),
    FormPart('Part 1', [
        FormQuestion('Question 1.1', details= 'just guess'),
    ]),
    FormPart('Part 2', [
        FormQuestion('Question 2.1'),
        FormPart('SubPart 1', [
            FormQuestion('Question 2.1.1', answers= ('Yes')),
        ]),
    ]),
    FormQuestion('Question 2'),
])

并访问它:

>>> form[0]
FormQuestion('Question 1', 'Why?', ('Because', 'Why not?'))
>>> form[1].title
'Part 1'
>>> form[2][1]
FormPart('SubPart 1', [FormQuestion('Question 2.1.1', '', 'Yes')])

现在,对于您的层次结构,您可以定义FormPart

    def getQuestions(self):
        for child in self:
            for descendant in child.getQuestions():
                yield descendant

和上FormQuestion

    def getQuestions(self):
        yield self

现在你有一个返回 FormQuestions 的后代生成器:

>>> list(form[1].getQuestions())
[FormQuestion('Question 1.1', 'just guess', ())]
>>> list(form.getQuestions())
[FormQuestion('Question 1', 'Why?', ('Because', 'Why not?')), FormQuestion('Question 1.1', 'just guess', ()), FormQuestion('Question 2.1', '', ()), FormQuestion('Question 2.1.1', '', 'Yes'), FormQuestion('Question 2', '', ())]
于 2009-11-23T06:12:20.610 回答
0

我想我会分享一些我从使用ElementTree中学到的东西,特别是ElementTree的lxml实现和带有一些 XPath的lxml.objectify 。XML 也可以简化为标签,其名称存储为属性。<part><question>

问题.xml

<myform>
    <question1>Question 1</question1>
    <part1 name="Part 1">
        <question1_1>Question 1.1</question1_1>
    </part1>
    <part2 name="Part 2">
        <question2_1 attribute="stuff">Question 2.1</question2_1>
        <subpart1 name="SubPart 1">
            <question2_1_1>Question 2.1.1</question2_1_1>
            <question2_1_2>Question 2.1.2</question2_1_2>
        </subpart1>
    </part2>
    <question2>Question 2</question2>
</myform>

问题.py

from lxml import etree
from lxml import objectify
# Objectify adds some python object-like syntax and other features.
# Important note: find()/findall() in objectify uses ETXPath, which supports
# any XPath expression. The union operator, starts-with(), and local-name()
# expressions below don't work with etree.findall.

# Using etree features
tree = objectify.parse('questions.xml')
root = tree.getroot()

# Dump root to see nodes and attributes
print etree.dump(root)

# Pretty print XML
print etree.tostring(root, pretty_print=True)

# Get part2 & all of its children
part2_and_children = root.findall(".//part2 | //part2//*")

# Get all Part 2 children
part2_children = root.findall(".//*[@name='Part 2']//*[starts-with(local-name(), 'question')]")

# Get dictionary of attributes for Question 2.1
list_of_dict_of_attributes = root.find(".//question2_1")[0].items()

# Access nodes like python objects
# Get all part2 question children
part2_question_children = root.part2.findall(".//*[starts-with(local-name(), 'question')]")

# Get text of question 2.1
text2_1 = root.part2.question2_1.text

# Get dictionary of attributes for Question 2.1
q2_1_attrs = root.part2.question2_1[0].items()
于 2009-11-25T07:56:14.070 回答