**之后的mypy参数必须是映射

时间:2018-09-13 22:48:52

标签: python python-3.x mypy

有一段代码可以循环构建问题对象,并为每个问题选择构建可能的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

并且创建者中的两个类都使用类型定义为QuestionMetaPossibleAnswerMeta的对象,这些对象在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的类型,questionDatavalue都具有相同的类型<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'>

1 个答案:

答案 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。