Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare a Protocol with a field which supports both a simple type and property?

(Related, but not duplicated: How to annotate attribute that can be implemented as property?)

I want to create a Protocol, in which a field can be implemented by both a simple type and property. For example:

class P(Protocol):
    v: int


@dataclass
class Foo(P):
    v: int


class Bar(P):
    @property
    def v(self) -> int: # ERROR
        return

But the code above doesn't type check. How should I fix it?

Note: I want to solve this issue without rewriting Foo and Bar, because Foo and Bar are not what I implemented.

According to this issue the below code is not a solution because read-only property and a simple member have subtly different semantics.

class P(Protocol):
    @property
    def v(self) -> int: # declare as property
        ...

Pyright denies this Protocol due to the difference.

like image 727
Yohei Avatar asked Sep 09 '25 20:09

Yohei


1 Answers

In general, declare the Protocol using a read-only property, not a read/write field:

class P(Protocol):
    @property
    def v(self) -> int:
        pass

This is needed because a read-only protocol attribute is satisfied by both a read-only property and a read/write field. In contrast, a read/write protocol attribute is satisfied only by a read/write field, not a read-only property.


As PyRight insists that fields and properties are different kinds of attributes, the attribute must be declared with both variants – once as a field and once as an attribute. For simple protocols, this can be done by declaring a separate field and property variant of the property:

# field only
class Pf(Protocol):
    v: int

# property only
class Pp(Protocol):
    @property
    def v(self) -> int:
        return 1

# Either field or property
P = Union[Pf, Pp]

This is valid for both MyPy and PyRight.

like image 200
MisterMiyagi Avatar answered Sep 12 '25 11:09

MisterMiyagi