mypy: how to verify a type has multiple super classes

☆樱花仙子☆ 提交于 2019-12-11 02:35:22

问题


I would like mypy to verify that a variable is subclassed from a certain base class and that it also has a specific mixin. Union only verifies that the value is of one type or another. I need to check that the value is both types.

In the example, I'm making up a keyword "All" to demonstrate the behavior I'm looking for:

from typing import All

class Base ( object ):
    pass
class Mixin ( object ):
    pass

def assert_all ( x ):
    # type: ( All[Base,Mixin] ) -> None
    assert isinstance ( x, Base ) and isinstance ( x, Mixin )

class Child ( Mixin, Base ):
    pass

assert_all ( Child() )
try:
    assert_all ( Base() ) # !!! mypy should complain here
except AssertionError:
    pass
else:
    raise AssertionError ( 'assert inside of assert_all() should have fired' )
try:
    assert_all ( Mixin() ) # !!! mypy should complain here, too
except AssertionError:
    pass
else:
    raise AssertionError ( 'assert inside of assert_all() should have fired' )

In case it's helpful, the reason I need this is I have my own win32 wrapper implemented in Python. My Base class is the basic Window class. My required Mixin is ControlHost which adds specific window styles and functions to the Window class to manage owning children, things that a basic window doesn't need. When creating child controls, like a ComboBox for example, I want mypy to call me out if the parent window I feed it doesn't have both super classes.

Also, I'm having an issue calling Window.init() from Control.init() because of a change of the parent's type. Here's abbreviated pseudo-code that illustrates the problem:

class Window:
    def __init__ ( self, parent, .... ):
        # type: ( Optional[Window], .... )

class Control ( Window ):
    def __init__ ( self, parent, .... ):
        # type: ( Optional[ControlHost], .... )

My work-around looks like this, but it doesn't allow mypy to catch type infractions:

class Control ( Control_mixin, Window ):
    ....

    def __init__ ( self, parent=None ):
        # type: ( Optional[ControlHost] ) -> None
        self.initStyle |= winapi.WS_CHILD|winapi.WS_CLIPSIBLINGS
        assert isinstance ( parent, Window )
        super ( Control, self ).__init__ ( cast ( Window, parent ) )

回答1:


What you're basically looking for here are "intersection types".

Unfortunately, mypy (and any other PEP 484-compliant type checkers) do not support intersection types.

There is, however, some discussion about adding such a type -- you can find some discussion on the typing/PEP 484 issue tracker.

Unfortunately, my understanding (after talking to the mypy core devs yesterday) is that while they agree such a type would be useful, it's pretty low on their priority list: adding intersection types would require a substantial amount of careful thought and implementation work. (For example, working out what happens when unions, intersections, and type variables are combined.)

It would probably be helpful if you tried contributing your example/use case to that thread -- if it turns out most people who want intersection types want it for specifically mixins or something, it may be possible for the mypy devs to think of a different way to support that specific use case without implementing full-fledged intersection types.


As an interim measure, you can perhaps use Protocols: define a protocol containing the methods from both the base and mixin classes and use that as the function's type.

(That said, I imagine your window classes have a lot of methods, so this may not be such a feasible solution in your case.)



来源:https://stackoverflow.com/questions/50319112/mypy-how-to-verify-a-type-has-multiple-super-classes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!