2009-11-23 29 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()