有一段代码可以循环构建问题对象,并为每个问题选择构建可能的Answers对象。
问题类别为:
class Question(graphene.ObjectType): # type: ignore
qId = graphene.String()
label = graphene.String()
question = graphene.String()
multipleAnswersAccepted = graphene.Boolean()
possibleAnswers = graphene.List(PossibleAnswer)
def __init__(self, questionObj: QuestionMeta) -> None:
print("__ questionObj type={y}".format(y=type(questionObj)))
self.qId = questionObj.qPointer
self.label = questionObj.label
self.question = questionObj.question
self.inputType = questionObj.inputType
self.multipleAnswersAccepted = questionObj.multipleAnswersAccepted
if questionObj.possibleAnswersPointer:
self.possibleAnswers = []
for _key, value in enumerate(questionObj.possibleAnswersPointer):
print("___possibleAnswersPointer is {}".format(
type(value)))
# x = PossibleAnswerMeta(**value)
possibleAnswer = PossibleAnswer(paObj=PossibleAnswerMeta(**value))
self.addPossibleAnswer(possibleAnswer)
else:
self.possibleAnswers = None
def addPossibleAnswer(self, possibleAnswer: Dict[int, str]) -> None:
"""Append possible-answer object to array of possible answers."""
self.possibleAnswers.append(possibleAnswer)
可能答案的类是
class PossibleAnswer(graphene.ObjectType): # type: ignore
paId = graphene.String()
text = graphene.String()
def __init__(self, paObj: PossibleAnswerMeta) -> None:
print("__ paObj type={y}".format(y=type(paObj)))
self.paId = paObj.paId
self.text = paObj.text
并且创建者中的两个类都使用类型定义为QuestionMeta
或PossibleAnswerMeta
的对象,这些对象在classes
模块中定义:
class PossibleAnswerMeta(NamedTuple):
text: str
paId: int
score: int
class QuestionMeta(NamedTuple):
qPointer: str
label: str
question: str
multipleAnswersAccepted: bool
possibleAnswersPointer: List[PossibleAnswerMeta]
创建问题的循环过程是:
for _questionNum, questionData in enumerate(value):
print("___questionData type is {}".format(type(questionData)))
q = Question(questionObj=QuestionMeta(**questionData))
s.addQuestion(question=q)
因此我们可以看到Question以questionObj=QuestionMeta(**questionData)
作为参数,而anysAnswer得到paObj=PossibleAnswerMeta(**value)
。
两者都是基于NamedTuple的类型,questionData
和value
都具有相同的类型<class 'dict'>
,但是mypy只抱怨值。
运行mypy时,我得到以下输出:
..79: error: Argument after ** must be a mapping
,其中第79行是possibleAnswer = PossibleAnswer(paObj=PossibleAnswerMeta(**value))
行。
问题: 代码可以正确运行, 正确创建了两个对象(我在石墨烯查询中看到了它们),参数类型相同,并打印在日志中
那么为什么mypy只抱怨传递值,而不是问问题数据,因为这两种类型都是od'dict'?
...
__ questionObj type=<class 'user.api.meta.classes.QuestionMeta'>
___possibleAnswersPointer is <class 'dict'>
__ paObj type=<class 'user.api.meta.classes.PossibleAnswerMeta'>
___possibleAnswersPointer is <class 'dict'>
__ paObj type=<class 'user.api.meta.classes.PossibleAnswerMeta'>
___possibleAnswersPointer is <class 'dict'>
__ paObj type=<class 'user.api.meta.classes.PossibleAnswerMeta'>
___questionData type is <class 'dict'>
__ questionObj type=<class 'user.api.meta.classes.QuestionMeta'>
___possibleAnswersPointer is <class 'dict'>
__ paObj type=<class 'user.api.meta.classes.PossibleAnswerMeta'>
___possibleAnswersPointer is <class 'dict'>
__ paObj type=<class 'user.api.meta.classes.PossibleAnswerMeta'>
答案 0 :(得分:1)
您的类型存在一个真正的问题,那就是mypy正确地警告了您。
让我们看一下输出中所有这样的行
___possibleAnswersPointer is <class 'dict'>
首先,不是possibleAnswersPointer
。您正在打印误导性消息;这是possibleAnswersPointer
元素的类。
第二,possibleAnswersPointer
的元素不应该是字典。您告诉mypy他们完全是别的东西:
possibleAnswersPointer: List[PossibleAnswerMeta]
您告诉mypy,possibleAnswersPointer
的元素将是PossibleAnswerMeta
的实例。 mypy绝对警告您PossibleAnswerMeta
的实例不是映射,并且不能与**
一起解压缩。您不会遇到运行时错误,因为possibleAnswersPointer
的元素不是您告诉mypy的元素。
如果不应该将possibleAnswersPointer
的元素作为PossibleAnswerMeta
的实例,则不要告诉mypy它们将是该类的实例。如果possibleAnswersPointer
的元素应该是PossibleAnswerMeta
的实例,那么您有mypy警告您的bug,以及导致这些元素成为dict的任何bug。