0

Assume I have below Mixin class, and it assumes the class inherits it has property value. However, PyLance is not happy with this code given self.value is not defined in foo_mixin

class foo_mixin:

    def f1(self, a:int) -> int:
        return self.value + a

    def f2(self, a:int) -> int:
        return self.f1(a) - a

Then I tried to type hint 'self' with some protocol, like below:

from typing import Protocol
class value_protocol(Protocol):
    @property
    def value(self) -> int: ...

And foo can is now defined as below:

class foo_mixin:

    def f1(self:value_protocol, a:int) -> int:
        return self.value + a

    def f2(self:value_protocol, a:int) -> int:
        return self.value * self.f1(a) - a

But value_protocol does not contain f1, so Pylance now is angry with me about f1 by saying

Cannot access member "f1" for type "value_protocol".
Member "f1" is unknown Pylance

My question is how should I type hint self in this case.

martineau
  • 119,623
  • 25
  • 170
  • 301
X.J.L
  • 65
  • 4
  • Wouldn't `self` by the Type of the class itself? Basically `def f1(self: foo)` – h0r53 Jul 15 '22 at 15:32
  • 1
    Given that this class has very specific dependencies on the classes that inherit it, are you sure you want to use a mixin instead of an ABC? or maybe dependency-injecting a `value_protocol` reference that doesn't necessarily have to be `self`? – Samwise Jul 15 '22 at 15:33
  • 1
    @h0r53, thanks for your comment, value should be defined in class inherits this mixin, so if foo is used as the type hint, Pylance will complain about Cannot access member "value" for type "foo_mixin". which is basically the same as not having any type hint I think. Please correct me if I'm wrong. – X.J.L Jul 15 '22 at 15:35
  • That's because `foo_mixin` isn't declared as inheriting another class. If you do that, then pylance should be happy. – h0r53 Jul 15 '22 at 15:36
  • @h0r53 `foo_mixin` is a [mixin](https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-is-it-useful). It's not inheriting anything nor being inherited by anything specific. – Axe319 Jul 15 '22 at 15:40
  • @Samwise thanks for your comment. that make sense. I may need to re-think about the design. However, would still be interested to know the best way to type hint self in such situation – X.J.L Jul 15 '22 at 15:48
  • If you annotate it as `Any` then no checking will be done at all on its attributes. I think if you want actual type safety an ABC is the better way to go though. – Samwise Jul 15 '22 at 15:52

1 Answers1

1

Make foo_mixin an ABC, with value an abstractproperty:

from abc import ABC, abstractproperty

class foo_mixin(ABC):

    @abstractproperty
    def value(self) -> int: ...

    def f1(self, a:int) -> int:
        return self.value + a

    def f2(self, a:int) -> int:
        return self.value * self.f1(a) - a

Now any concrete class which inherits foo_mixin is required to implement value, which makes it safe for any instance of foo_mixin to access self.value.

Samwise
  • 68,105
  • 3
  • 30
  • 44
  • Interesting. Thanks for the answer. Will accept answer. but just out of curiosity, do you happen to know if this approach has more overhead than type hint? My understanding is abc will create some overhead, but type hint shouldn't have any impact on run time performance. – X.J.L Jul 15 '22 at 16:06
  • The only overhead is that incurred in verifying that the subclass definition implements `value`. Instances of valid subclasses don't behave any differently. – chepner Jul 15 '22 at 16:09
  • `@abstractproperty` was deprecated in 3.3 for `@abstractmethod` and `@property` – joel Jul 15 '22 at 18:10