- question: "What is your name?"
  type: text
  required: true

- question: "How old are you?"
  type: number
  required: true
  min_value: 18
  max_value: 100

- question: "What is your gender?"
  type: multiple_choice
  choices:
    - Male
    - Female
    - Other
  required: true

- question: "Do you have any dietary restrictions?"
  type: checkboxes
  choices:
    - Vegetarian
    - Vegan
    - Gluten-free
    - Dairy-free
    - None

- question: "Which programming languages do you know?"
  type: checkboxes
  choices:
    - Python
    - JavaScript
    - Java
    - C++
    - Ruby

- question: "How satisfied are you with our product?"
  type: scale
  min_value: 1
  max_value: 5

- question: "Any additional comments or feedback?"
  type: textarea

这上面的YAML是一个非常小的问卷版本,它涉及到近70+个问题.我想设计一个可以读取上述YAML的类,并使其具有动态可伸缩性和可读性. 该类还必须处理数据输入验证,并且必须为阅读它的任何人提供足够的信息. 我怎么能这样做呢?

编辑:

from enum import Enum
import yaml


class QuestionKind(Enum):
    TEXT = "text"
    NUMBER = "number"
    MULTIPLE_CHOICE = "multiple_choice"
    CHECKBOXES = "checkboxes"
    SCALE = "scale"
    TEXTAREA = "textarea"


class Question:
    def __init__(self, prompt, kind, choices=None):
        self.prompt = prompt
        self.kind = kind
        self.choices = choices


class Quiz:
    def __init__(self, questions_file):
        self.questions = self.load_questions(questions_file)
        self.current_index = 0

    def load_questions(self, questions_file):
        with open(questions_file, "r") as file:
            data = yaml.safe_load(file)
        
        questions = []
        for item in data:
            prompt = item["question"]
            kind = QuestionKind(item["type"])
            choices = item.get("choices")
            question = Question(prompt, kind, choices)
            questions.append(question)
        
        return questions

    def current_question(self):
        if self.current_index < len(self.questions):
            return self.questions[self.current_index]
        else:
            return None

    def provide_answer(self, answer):
        self.current_index += 1
        # Process the answer as needed

        return self.current_question()


# Usage example
quiz = Quiz("questions.yaml")
current_question = quiz.current_question()
while current_question is not None:
    print("Question:", current_question.prompt)
    answer = input("Your answer: ")
    current_question = quiz.provide_answer(answer)

我正在考虑做这样的事情--这是正确的方式吗?

编辑:这会是改进的方法吗?

import sys
from pathlib import Path
import ruamel.yaml

file_in = Path('questions.yaml')
print(file_in.read_text(), end='===============\n')


class BaseQuestion:
    def __init__(self, question, required=False, conditions=None):
        self._question = question
        self._required = required
        self._value = None  # to store user response to question
        self._conditions = conditions or []

    @classmethod
    def from_yaml(cls, constructor, node):
        kw = ruamel.yaml.CommentedMap()
        constructor.construct_mapping(node, kw)
        return cls(**kw)

    def check_conditions(self, responses):
        for condition in self._conditions:
            question_id = condition.get('id')
            operator = condition.get('operator')
            value = condition.get('value')
            if question_id in responses and self._compare_values(responses[question_id], operator, value):
                return False
        return True

    def _compare_values(self, value1, operator, value2):
        """Compare two values based on the given operator"""
        if operator == "==":
            return value1 == value2
        elif operator == "!=":
            return value1 != value2
        elif operator == ">":
            return value1 > value2
        elif operator == "<":
            return value1 < value2
        elif operator == ">=":
            return value1 >= value2
        elif operator == "<=":
            return value1 <= value2
        else:
            raise ValueError(f"Invalid operator: {operator}")

    def __repr__(self):
        return f'{self.yaml_tag}("{self._question}", required={self._required}, conditions={self._conditions})'


class BaseTextQuestion(BaseQuestion):
    yaml_tag = '!Text'

    def __init__(self, question, required=False, conditions=None):
        super().__init__(question=question, required=required, conditions=conditions)


class TextQuestion(BaseTextQuestion):
    yaml_tag = '!Text'


class TextAreaQuestion(BaseTextQuestion):
    yaml_tag = '!TextArea'


class NumberQuestion(BaseQuestion):
    yaml_tag = '!Number'

    def __init__(self, question, min_value, max_value, required=False, conditions=None):
        super().__init__(question=question, required=required, conditions=conditions)
        self._min = min_value
        self._max = max_value

    def check(self, responses):

        if not self.check_conditions(responses):
            return False
        if self._required and self._value is None:
            return False
        if self._value is None:
            return True
        return self._min <= self._value <= self._max

    def __repr__(self):
        return f'{self.yaml_tag}("{self._question}", range=[{self._min}, {self._max}], required={self._required}, conditions={self._conditions})'


class ScaleQuestion(NumberQuestion):
    yaml_tag = '!Scale'


class ChoiceQuestion(BaseQuestion):
    def __init__(self, question, choices, required=False, conditions=None):
        super().__init__(question=question, required=required, conditions=conditions)
        self._choices = choices

    def __repr__(self):
        return f'{self.yaml_tag}("{self._question}", choices=[{", ".join(self._choices)}], required={self._required}, conditions={self._conditions})'


class MultipleChoiceQuestion(ChoiceQuestion):
    yaml_tag = '!MultipleChoice'


class CheckBoxesQuestion(ChoiceQuestion):
    yaml_tag = '!CheckBoxes'


yaml = ruamel.yaml.YAML()
yaml.register_class(TextQuestion)
yaml.register_class(TextAreaQuestion)
yaml.register_class(NumberQuestion)
yaml.register_class(ScaleQuestion)
yaml.register_class(MultipleChoiceQuestion)
yaml.register_class(CheckBoxesQuestion)

questions = yaml.load(file_in)


def display_questions(questions):
    """Display the questions based on the conditions and user responses"""
    responses = {}
    for question in questions:
        if question.check_conditions(responses):
            response = input(question._question + " ")
            responses[question.__dict__.get("_id")] = response
    print("User Responses:", responses)


display_questions(questions)

推荐答案

您不应该创建一个解释YAML的类,而应该使用YAML的工具来标记每个问题 这会使它作为不同类的适当实例加载,并取消type键.

这些类需要向YAML加载器注册,并且可以基于公共基类或某些 类层次 struct ,因此它可以实现通用行为.

在下面的代码中,我将更新后的YAML保存在一个名为questions.yaml的文件中:

import sys
from pathlib import Path
import ruamel.yaml

file_in = Path('questions.yaml')
print(file_in.read_text(), end='===============\n')

class BaseQuestion:
    def __init__(self, question, required=False):
        self._question = question
        self._required = required
        self._value = None  # to store user response to question

    @classmethod
    def from_yaml(cls, constructor, node):
        kw = ruamel.yaml.CommentedMap()
        constructor.construct_mapping(node, kw)
        return cls(**kw)

    def __repr__(self):
        return(f'{self.yaml_tag}("{self._question}", required={self._required})')

class BaseTextQuestion(BaseQuestion):
    yaml_tag = '!Text'

    def __init__(self, question, required=False):
        super().__init__(question=question, required=required)

class TextQuestion(BaseTextQuestion):
    yaml_tag = '!Text'

class TextAreaQuestion(BaseTextQuestion):
    yaml_tag = '!TextArea'

class NumberQuestion(BaseQuestion):
    yaml_tag = '!Number'

    def __init__(self, question, min_value, max_value, required=False):
        super().__init__(question=question, required=required)
        self._min = min_value
        self._max = max_value

    def check(self):
        """return False if value not in range or required and not set"""
        if self._required and self._value is None:
            return False
        if self._value is None:
            return True
        return self._min <= self._value <= self._max

    def __repr__(self):
        return(f'{self.yaml_tag}("{self._question}", range=[{self._min}, {self._max}], required={self._required})')

class ScaleQuestion(NumberQuestion):
    yaml_tag = '!Scale'

class ChoiceQuestion(BaseQuestion):
    def __init__(self, question, choices, required=False):
        super().__init__(question=question, required=required)
        self._choices = choices

    def __repr__(self):
        return(f'{self.yaml_tag}("{self._question}", choices=[{", ".join(self._choices)}], required={self._required})')


class MultipleChoiceQuestion(ChoiceQuestion):
    yaml_tag = '!MultipleChoice'

class CheckBoxesQuestion(ChoiceQuestion):
    yaml_tag = '!CheckBoxes'



yaml = ruamel.yaml.YAML()
yaml.register_class(TextQuestion)
yaml.register_class(TextAreaQuestion)
yaml.register_class(NumberQuestion)
yaml.register_class(ScaleQuestion)
yaml.register_class(MultipleChoiceQuestion)
yaml.register_class(CheckBoxesQuestion)
questions = yaml.load(file_in)
for q in questions:
    print(q)

这提供了:

- !Text
  question: "What is your name?"
  required: true

- !Number
  question: "How old are you?"
  required: true
  min_value: 18
  max_value: 100

- !MultipleChoice
  question: "What is your gender?"
  choices:
    - Male
    - Female
    - Other
  required: true

- !CheckBoxes
  question: "Do you have any dietary restrictions?"
  choices:
    - Vegetarian
    - Vegan
    - Gluten-free
    - Dairy-free
    - None

- !CheckBoxes
  question: "Which programming languages do you know?"
  choices:
    - Python
    - JavaScript
    - Java
    - C++
    - Ruby

- !Scale
  question: "How satisfied are you with our product?"
  min_value: 1
  max_value: 5

- !TextArea
  question: "Any additional comments or feedback?"
===============
!Text("What is your name?", required=True)
!Number("How old are you?", range=[18, 100], required=True)
!MultipleChoice("What is your gender?", choices=[Male, Female, Other], required=True)
!CheckBoxes("Do you have any dietary restrictions?", choices=[Vegetarian, Vegan, Gluten-free, Dairy-free, None], required=False)
!CheckBoxes("Which programming languages do you know?", choices=[Python, JavaScript, Java, C++, Ruby], required=False)
!Scale("How satisfied are you with our product?", range=[1, 5], required=False)
!TextArea("Any additional comments or feedback?", required=False)

从上面开始,您可以添加适当地显示每个类的方法 您的应用程序,将check添加到NumberQuestion以外的其他类中,等等. 您可以创建一个保存序列的Questions类,甚至可以注册它以加载它,但是 在本例中,这不是我要做的,只是使用从根加载的列表 YAML级别序列.

Python相关问答推荐

是什么导致对Python脚本的jQuery Ajax调用引发500错误?

如何在Python中使用ijson解析SON期间检索文件位置?

通过交换 node 对链接列表进行 Select 排序

Pandas :多索引组

在函数内部使用eval(),将函数的输入作为字符串的一部分

试图找到Python方法来部分填充numpy数组

如何删除索引过go 的lexsort深度可能会影响性能?' &>

删除任何仅包含字符(或不包含其他数字值的邮政编码)的观察

查找两极rame中组之间的所有差异

如何过滤包含2个指定子字符串的收件箱列名?

如何更改分组条形图中条形图的 colored颜色 ?

Asyncio:如何从子进程中读取stdout?

如何在图中标记平均点?

如何使regex代码只适用于空的目标单元格

AES—256—CBC加密在Python和PHP中返回不同的结果,HELPPP

Pandas:计算中间时间条目的总时间增量

Python—压缩叶 map html作为邮箱附件并通过sendgrid发送

无法在Spyder上的Pandas中将本地CSV转换为数据帧

GPT python SDK引入了大量开销/错误超时

Pandas:将值从一列移动到适当的列