2009-11-23 28 views
1

我只是回到编码后几年hiatus,我试图模拟多层静态窗体的方式,让我抓住和执行操作特定的表单级别或整个子树。Python数据结构/对象建模静态多维表

示例形式的层次结构:

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

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

感谢您的任何意见。

回答

2

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

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

http://effbot.org/zone/element-index.htm

(还有一些其他成熟的解决方案,以加载XML文件转换成不同的数据结构,随便挑一个是最容易使用你的任务是谷歌。你的朋友。:-))

+0

谢谢。我阅读了lxml,目前正在研究http://codespeak.net/lxml/objectify.html,它使得XML访问行为与Python对象相似。不知道它是否适合我的所有需求,但将不得不玩。 – 2009-11-23 06:19:53

+0

我同意。尝试lxml是一个非常好的主意。我自己使用了lxml,它比Python的标准库中的ElementTree更好。当你来到XML命名空间的支持时尤其如此...... – fviktor 2009-11-24 12:04:18

2

没有什么headachey或虐待支持有关在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...]),并使用正常的列表索引和切片访问里面的问题和formparts。

接下来,一个问题可能会像一个元组不可改变的事情,但也许这是你希望的项目有很好的属性名称。所以添加到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', '',())] 
+0

非常感谢。你让我考虑扩展内置的数据结构,而不是试图让我的问题适应已有的内容。我不确定自己为定义表单而提出的特定格式很疯狂,但这是向我开放想法的巨大飞跃。再次感谢! – 2009-11-23 20:13:28

0

想我会分享一些什么,我已经从这样使用ElementTree的教训,特别是lxml执行ElementTreelxml.objectify与一些XPath。该XML也可以简化为<part><question>标签存储为属性的名称。

questions.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()