structural subtyping in python with Protocol! (intermediate) anthony explains #164

preview_player
Показать описание
today I talk about one of my favorite additions to typing: Protocol! in this we extend from a previous example where we used overload to more-properly support any "__index__"-able type!

==========

I won't ask for subscriptions / likes / comments in videos but it really helps the channel. If you have any suggestions or things you'd like to see please comment below!
Рекомендации по теме
Комментарии
Автор

I know this is quite a late comment but amazing video as always. I only found out your channel about a few weeks ago, and just keep randomly watching your historical videos and learn heaps from it. The best python video on youtube for intermediate to advanced topics imo.

I have a question in regards to implementing an interface, hoping you could give me some guidance. How would you choose between using `abc` and `protocol`? What are the factors in your consideration?

For example, I know if there is a inheriting requirements (eg. I want to implement a template pattern in the parent class (interface) so that all the child classes can use the same methods), then `abc` should be the conventional way to go (although I can still inherit from a parent class using protocol, but it's not designed for it I suppose). Other than this, what are other considerations in choosing between abc and interface? something like if ..., I should use `abc`, elIf ..., I should use `protocol`, else: prefer `protocol` over `abc` (or vice versa)?

alsonyang
Автор

Thank you for another great video! Is there a benefit for using a Protocol for an interface as opposed inheriting an ABC?

OrCarmi
Автор

Amazing, thanks!

If you made a course on type annotations I'm sure I'm not the only one who'd pay good money for it

marco_gorelli
Автор

i guess you forgot to mention you need to do from __future__ import annotations or wrap your protocol class name in a string for it to work?

sadhlife
Автор

Hello.
Could you please clarify the proper way of annotating for a dict of classes each of which must implement Protocol is?

Here is an example:
```
from typing import NoReturn, Protocol

from .ce import CEDriver
from .mellanox import MellanoxDriver


class SupportsGetSet(Protocol):
def get_state(self) -> str:
...

def set_state(self, desired_stete: str) -> NoReturn:
...


driver_map: dict[str, SupportsGetSet] = {
"huawei": CEDriver,
"mellanox": MellanoxDriver,
}
```

In this case, mypy yells at me with:
```
error: Dict entry 0 has incompatible type "str": "Type[CEDriver]"; expected "str": "SupportsGetSet"
error: Dict entry 1 has incompatible type "str": "Type[MellanoxDriver]"; expected "str": "SupportsGetSet"
```

This is understandable since it expects an object which implements Protocol. So if I do this:
```
driver_map: dict[str, Type[SupportsGetSet]] = {
"huawei": CEDriver,
"mellanox": MellanoxDriver,
}
```
mypy becomes happy at this point but yells at me when I actually call a class:
```
error: Unexpected keyword argument "host" for "SupportsGetSet"
error: Unexpected keyword argument "interface" for "SupportsGetSet"
error: "SupportsGetSet" has no attribute "__aenter__"
error: "SupportsGetSet" has no attribute "__aexit__"
```
So it is like it expects me to describe __init__ and __aenter__/__aexit__ which I don't relly want to do :P

I even tried to use a bounded TypeVar:
```
PortSwitcherDriver = TypeVar("PortSwitcherDriver", bound=SupportsGetSet)

driver_map: dict[str, PortSwitcherDriver] = {
"huawei": CEDriver,
"mellanox": MellanoxDriver,
}
```
but now it says:
```
error: Type variable is unbound
note: (Hint: Use "Generic[PortSwitcherDriver]" or base class to bind "PortSwitcherDriver" inside a class)
note: (Hint: Use "PortSwitcherDriver" in function signature to bind "PortSwitcherDriver" inside a function)
```

What is the right way to achieve the goal? Thank you in advance =)

horseinthesky