Mypy: Mypy disallows overriding an attribute with a property

Created on 16 Oct 2017  路  10Comments  路  Source: python/mypy

For example:

class Foo:
    def __init__(self) -> None:
        self.x = 42

class Bar(Foo):
    def __init__(self) -> None:
        self.y = 9000

    @property
    def x(self) -> int:
        print('got x!!!')
        return self.y

    @x.setter
    def x(self, value: int) -> None:
        print('set x!!!')
        self.y = value

This should be allowed as it doesn't violate LSP as long as x implements both a getter and a setter of the correct type.

bug false-positive priority-0-high refactoring

Most helpful comment

Setting priority to high, see duplicate issue https://github.com/python/mypy/issues/6759#issuecomment-521695300 for motivation.

All 10 comments

Hm, interesting, this is already allowed for structural subtypes:

class Foo(Protocol):
    x: int

class Bar:
    def __init__(self) -> None:
        self.y = 9000

    @property
    def x(self) -> int:
        print('got x!!!')
        return self.y

    @x.setter
    def x(self, value: int) -> None:
        print('set x!!!')
        self.y = value

x: Foo = Bar() # OK

I think this is a bug, it should be allowed for nominal classes as well.

Yes, this is a bug. I was under the impression that this was already working.

Technically it does violate LSP; there should be a warning about the missing @x.deleter.

Yes, but if you add a deleter it still complains.

I don't think mypy should actually care about whether a deleter is present though, since deleting attributes is rare and if you do it your code is unlikely to be type-safe anyway.

True, but overriding an attribute with a property is also not so common, so a reminder that it's not complete might be useful.

There is a different error message when using property() as a function:

class Base:
    prop: bool

class Sub(Base):
    def _get_prop(self) -> bool:
        return False
    def _set_prop(self, value: bool) -> None:
        pass
    prop = property(_get_prop, _set_prop)
foo.py:9: error: Incompatible types in assignment (expression has type "property", base class "Base" defined the type as "bool")

In mypy==0.720, if I slightly modify the example in https://github.com/python/mypy/issues/4125#issuecomment-337048971 (above) based on the documentation's guidance that:

Explicitly including a protocol as a base class is also a way of documenting that your class implements a particular protocol, and it forces mypy to verify that your class implementation is actually compatible with the protocol.

...I get the following behavior:

class Foo(Protocol):
    x: int

class Bar(Foo):
    def __init__(self) -> None:
        self.y = 9000

    @property            # Signature of "x" incompatible with supertype "Foo"
    def x(self) -> int:  # Signature of "x" incompatible with supertype "Foo"
        print('got x!!!')
        return self.y

    @x.setter
    def x(self, value: int) -> None:
        print('set x!!!')
        self.y = value

x: Foo = Bar()

I would expect this to be OK for the same reasons that the original protocol example (no inheritance) is OK.

Setting priority to high, see duplicate issue https://github.com/python/mypy/issues/6759#issuecomment-521695300 for motivation.

I ran into a similar issue. Mypy seems to disallow overriding even a property, if not defined using a decorator:

# Annotations in here doesn't seem to matter.
def _not_implemented(*args, **kwargs):
    raise NotImplementedError


class AbstractClass:
    foo = property(_not_implemented, _not_implemented)


class MyClass(AbstractClass):
    # Here the mypy reports:
    # error: Signature of "foo" incompatible with supertype "AbstractClass"  [override]
    @property
    def foo(self) -> str:
        return 'Foo'

    @foo.setter
    def foo(self, value: str) -> None:
        pass

If AbstractClass.foo is defined with property as a decorator, it works fine.

Was this page helpful?
0 / 5 - 0 ratings