mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Consider allowing `@final` methods in Protocols

Open tmke8 opened this issue 3 years ago • 3 comments

Feature

Allow this:

from typing import Protocol, final

class Sized(Protocol):
    len: int
    @final
    def __len__(self) -> int:
        return self.len

Pitch

I get that this is a bit unusual, but I don’t really see any harm that could come from this (as an aside, pyright allows it). My use case is that I’m using Protocols for mixins, and sometimes I want to make sure my mixed-in methods don’t get overwritten.

tmke8 avatar Aug 02 '22 19:08 tmke8

If you are using protocols for mixins, why not ABCs? To me, @final and non-inheritance subtyping (meaning you can provide a different implementation) feels like it's just asking for trouble although I can't think of any cases negatively impacted by this.

OTOH ABCs also offer virtual registering and I didn't check mypy for whether @final is allowed so the "bad" behavior might already be there (if mypy were to support that registering, that is :-).

A5rocks avatar Aug 03 '22 06:08 A5rocks

If you are using protocols for mixins, why not ABCs?

With ABCs, there is the problem that I can't define abstract attributes, like the len: int in the example code above. I raised the question of abstract attributes in ABCs on typing-sig: https://mail.python.org/archives/list/[email protected]/thread/UAL6LMKDLUNFXSR7HGOXPK2KUFIROJES/ but there wasn't much interest for it.

Yeah, virtual registering really is a strange feature. I feel like most people just pretend it doesn't exist.

tmke8 avatar Aug 07 '22 19:08 tmke8

I feel like this should be separated into two parts:

from typing import Protocol, final

class MySized(Protocol):
    len: int
    def __len__(self) -> int: ...

class _MyImpl:
   def __len__(self) -> int:
       return self.len

class MyClass(_MyImpl):
   def __init__(self, len: int) -> None:
       self.len = len

I would prefer to keep Protocols free of implementation. They are signature declarations to me (just pure structure).

sobolevn avatar Aug 09 '22 09:08 sobolevn

If I'd like to share the default implementation across protocol implementations, does it mean I have to do something like this:

import abc
from typing import Protocol, final

class MySized(Protocol):
    len: int
    def __len__(self) -> int: ...

class _MyDefaultImpl(abc.ABC, MySized):
   @final
   def __len__(self) -> int:
       return self.len

class MyClass(_MyDefaultImpl):
   def __init__(self, len: int) -> None:
       self.len = len

MyClass(15)

zhukovgreen avatar Jan 11 '24 12:01 zhukovgreen