2009-11-23 2 views
1

Я только вернусь к кодированию после нескольких лет перерыва, и я пытаюсь моделировать многоуровневые статические формы таким образом, чтобы я мог захватывать и выполнять операции на определенного уровня формы или всего поддерева.Структура/объект данных 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. Более подробную информацию можно найти здесь:

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

(Там уже другие зрелые решения в Python, чтобы загрузить XML-файл в различные структуры данных Просто выберите тот, который является самым простым в использовании для вашей задачи Google является.. ваш друг. :-))

+0

Спасибо. Я прочитал lxml и в настоящее время просматриваю http://codespeak.net/lxml/objectify.html, что делает доступ XML доступным как объекты Python. Не уверен, что он подойдет всем моим потребностям, но мне придется поиграть. –

+0

Согласен. Попытка lxml - очень хорошая идея. Я сам использовал lxml, и он работал лучше, чем ElementTree из стандартной библиотеки Python. Это особенно верно, когда вы приходите в поддержку пространства имен XML ... – fviktor

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...]) и получить доступ вопросы и 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

Спасибо за тонну. Вы заставили меня задуматься о расширении встроенных структур данных, а не пытаться подогнать мою проблему к уже имеющейся. Не уверен, что я сумасшедший о конкретном формате, который вы придумали для определения формы, но это огромный скачок в открытии идей для меня. Еще раз спасибо! –

0

Мысль, что я поделюсь немного о том, что я узнал от делать это с помощью ElementTree, в частности lxml реализация ElementTree и lxml.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() 
Смежные вопросы