Python数据结构/对象对静态多维表进行建模

发布于 2024-08-12 10:57:48 字数 1384 浏览 6 评论 0原文

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

表单层次结构示例:

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

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

感谢您的任何评论。

I'm just getting back into coding after a few year hiatus and I'm trying to model multi-tiered static forms in a way that lets me grab and perform operations on a specific form level or an entire sub-tree.

Example Form hierarchy:

  • MyForm
    • Question 1
    • Part 1
      • Question 1.1
    • Part 2
      • Question 2.1
      • SubPart 1
        • Question 2.1.1
        • Question 2.1.2
    • Question 2

Each Question will have multiple attributes (question text, whether it's a required field, etc.) and Questions can be at any level of the hierarchy.

I'd like to be able to do something like this:

>>> 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]

and/or stuff like:

>>> # 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))

I keep trying to do this with nested/inner classes, which are a big headache and not well-supported in python. But even if I can figure out a solution using nested classes, I keep wondering whether there's a much better way of storing this form info somewhere to make it easier for non-coders to edit (probably a plain text template), and then loading the data at run-time since it's static and I'll need it in memory quite often. The form data won't be updated more than say once per month. Regardless how I store the data, I'd like to figure out a good data structure to represent, traverse, and operate on it.

  • Is there a way to make a tiered-attributes object like this?
  • Could I do something like multidimensional named tuples?
  • Any other ideas?

Thanks for any comments.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无可置疑 2024-08-19 10:57:48

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

请参阅 Python 手册中的 xml.etree.ElementTree。更多信息可以在此处:(

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

I'd store such hierarchical data in XML on the storage. You can use the xml.etree.ElementTree standard module to load such an XML file into a hierarchical data structure in Python, make changes to it, then save it back to a file. This way you don't have to bother with the actual data structure, since it is built by ElementTree automatically.

See xml.etree.ElementTree in the Python Manual. More information can be found here:

(There're other mature solutions in Python to load an XML file into various data structures. Just pick one which is the easiest to use for your task. Google is your friend. :-) )

梦纸 2024-08-19 10:57:48

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

There's nothing headachey or ill-supported about nested classes in Python, it's just they don't do anything. Don't expect to get a Java-inner-class-style link back to an owner instance automatically: nested classes are nothing but normal classes whose class object happens to be stored in the property of another class. They don't help you here.

Is there a way to make a tiered-attributes object like this?

Certainly, but you'd probably be better off extending Python's existing sequence classes to get the benefits of all the existing operations on them. For example, a form ‘part’ might simply be a list which also has a title:

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))

Now you can say form= FormPart('My form', [question, formpart...]) and access the questions and formparts inside it using normal list indexing and slicing.

Next, a question might be an immutable thing like a tuple, but perhaps you want the items in it to have nice property names. So add that to 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))

Now you can define your data like:

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

And access it:

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

Now for your hierarchy-walking you can define on FormPart:

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

and on FormQuestion:

    def getQuestions(self):
        yield self

Now you've got a descendant generator returning 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', '', ())]
匿名。 2024-08-19 10:57:48

我想分享一些我从使用 ElementTree 中学到的东西,特别是 ElementTreelxml 实现lxml.objectify 带有一些 XPath。 XML 还可以简化为 标签,并将名称存储为属性。

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>

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

Thought I'd share a bit of what I've learned from doing this using ElementTree, specifically the lxml implementation of ElementTree and lxml.objectify with some XPath. The XML could also be simplified to <part> and <question> tags with names stored as attributes.

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>

questions.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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文