【问题标题】:Python data structure/object to model static multidimensional tablePython数据结构/对象建模静态多维表
【发布时间】:2010-12-19 08:49:24
【问题描述】:

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

示例表单层次结构:

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

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

感谢任何cmets。

【问题讨论】:

    标签: python class data-structures data-modeling inner-classes


    【解决方案1】:

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

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

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

    【讨论】:

    • 谢谢。我阅读了 lxml,目前正在查看codespeak.net/lxml/objectify.html,它使 XML 访问的行为类似于 Python 对象。不确定它是否能满足我的所有需求,但必须玩。
    • 我同意。尝试 lxml 是一个非常好的主意。我自己经常使用 lxml,它比 Python 标准库中的 ElementTree 效果更好。当您谈到 XML 命名空间支持时尤其如此……
    【解决方案2】:

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

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

    当然可以,但是您最好扩展 Python 的现有序列类,以从对它们的所有现有操作中获益。例如,表单“部分”可能只是一个也有标题的列表:

    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', '', ())]
    

    【讨论】:

    • 非常感谢。你让我考虑扩展内置的数据结构,而不是试图让我的问题适应现有的问题。不确定我是否对您为定义表单提出的特定格式感到疯狂,但这是向我开放想法的巨大飞跃。再次感谢!
    【解决方案3】:

    我想分享一些我从使用 ElementTree 中学到的东西,特别是 ElementTreelxml 实现和lxml.objectify 和一些 XPath。 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-04
      • 2022-01-02
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 2012-01-28
      • 2014-08-14
      相关资源
      最近更新 更多