1

The mypy docs read

Callback protocols and :py:data:~typing.Callable types can be used interchangeably. Keyword argument names in :py:meth:__call__ <object.__call__> methods must be identical, unless a double underscore prefix is used. For example:

typing_extensions import Protocol

T = TypeVar('T')

class Copy(Protocol):
    def __call__(self, __origin: T) -> T: ...

copy_a: Callable[[T], T]    copy_b: Copy

copy_a = copy_b  # OK    copy_b = copy_a  # Also OK    ```

However, this example also works if we remove the double underscore prefix from before __origin. E.g.

$ cat t.py 
from typing import Callable, TypeVar
from typing_extensions import Protocol

T = TypeVar('T')

class Copy(Protocol):
    def __call__(self, origin: T) -> T: ...

copy_a: Callable[[T], T]
copy_b: Copy

copy_a = copy_b  # OK
copy_b = copy_a  # Also OK
$ mypy t.py 
Success: no issues found in 1 source file

So, this example isn't clear to me. When do we need the double underscore prefix?

ignoring_gravity
  • 6,677
  • 4
  • 32
  • 65

1 Answers1

1

A named parameter can be used in placed of an anonymous parameter, so the Protocol works as a value for the Callable. The first assignment copy_a = copy_b silently promotes copy_a to a Copy, which is then valid to assign to copy_b: Copy.

...

copy_a = copy_b  # copy_a is a Copy now!
copy_b = copy_a  # assign copy_a: Copy to copy_b: Copy
reveal_type(copy_a)  # Revealed type is 'aaa_testbed.Copy'

Swapping the assignment means copy_b = copy_a happens while copy_a is still the anonymous type. This triggers the expected error:

...

copy_b = copy_a  # Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Copy")
copy_a = copy_b
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119