How can I assign values with mypy and @property?

Viewed 1529

I'm using the @property decorator and I assign a value to that property. However, mypy throws an error with my code (which works). Do I do something wrong? How can I fix my code so that mypy doesn't complain?

MVCE

# Core Library
from abc import ABCMeta, abstractmethod


class ListNode(metaclass=ABCMeta):
    def __init__(self, value):
        """Every list node should have a value at initialization."""

    @property
    @abstractmethod
    def value(self):
        """Read the value attribute"""

    @abstractmethod
    def next(self):
        """Read the next attribute"""


class SinglyLinkedListNode(ListNode):
    def __init__(self, value):
        self.value = value
        self.next = None  # Optional[SinglyLinkedListNode]

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        self._value = value

    @property
    def next(self):
        return self._next

    @next.setter
    def next(self, next_):
        self._next = next_


def reverse(list_node: ListNode) -> ListNode:
    """Reverse a list."""
    current = list_node
    previous = None

    while current is not None:
        previous, current.next, current = current, previous, current.next  # This throws the error

    return previous

Error:

mpu/datastructures/lists.py:47: error: Cannot assign to a method
mpu/datastructures/lists.py:47: error: Incompatible types in assignment (expression has type "ListNode", variable has type "Callable[[], Any]")
mpu/datastructures/lists.py:47: error: Incompatible types in assignment (expression has type "Callable[[], Any]", variable has type "ListNode")
1 Answers

In your example there is probably a typo, instead of

self.next = None

it should be

self._next = None

Because we are overwriting property with field your code seems to work, but not probably how it was intended.

Also it seems that ListNode.next should be a property too like

class ListNode(metaclass=ABCMeta):
    ...

    @property
    @abstractmethod
    def next(self):
        """Read the next attribute"""

Finally adding setter for ListNode.next

    @next.setter
    def next(self, next_):
        """Write the next attribute"""

makes errors to disappear.


BTW decorating ListNode.next setter with abstractmethod causes errors

> mypy test.py
test.py:14: error: Overloaded method has both abstract and non-abstract variants
test.py:19: error: Decorated property not supported
test.py:53: error: Property "next" defined in "ListNode" is read-only

and seems to be redundant, because removing setter for SinglyLinkedListNode.next causes

> mypy test.py
test.py:37: error: Read-only property cannot override read-write property

Also for type comment to work we need to add type: prefix

class SinglyLinkedListNode(ListNode):
    def __init__(self, value):
        self.value = value
        self._next = None  # type: Optional[SinglyLinkedListNode]

(we can also add from typing import Optional, because PyCharm isn't satisfied saying "Unresolved reference 'Optional'", but it seems to be ok for mypy).

Related