В чём разница между и ? С помощью чего в питоне делать переопределение?
question@mail.ru
·
01.01.1970 03:00
Разница между @singledispatch и @overload
answer@mail.ru
·
01.01.1970 03:00
tl;dr:
@overloadтолько декларирует (для линтеров или IDE) разные возможные сочетания аннотаций типов для одной функции@singledispatchвыполняет переключение на нужную реализацию функции в зависимости от фактических типов аргументов
Наиболее краткое и понятное описание декоратора overload нашел :
def overload(func): """"""Decorator for overloaded functions/methods. In a stub file, place two or more stub definitions for the same function in a row, each decorated with @overload. For example: @overload def utf8(value: None) -> None: ... @overload def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... In a non-stub file (i.e. a regular .py file), do the same but follow it with an implementation. The implementation should *not* be decorated with @overload. For example: @overload def utf8(value: None) -> None: ... @overload def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... def utf8(value): # implementation goes here """""" retu _overload_dummyСмысл такой:
1. Если используется в стаб-файле (pyi)
Для одной и той же функции можно указать, что она может принимать разные типы аргументов. Т.е. реализация одна, а деклараций несколько:
Файл pyi
@overloaddef some_function(value: int) -> int: ...@overloaddef some_function(value: float) -> float: ...@overloaddef some_function(value: str) -> str: ...В py файле:
def some_function(value): if isinstance(value, int): retu value + 1 elif isinstance(value, float): retu value * 2.0 elif isinstance(value, str): retu ""Hello, "" + value else: raise TypeError2. Если используется в py-файле
- аналогично, декларирует различные варианты типов, в самих декларациях не должно быть реализации, а у реализации не должно быть декоратора @overload (""In a non-stub file (i.e. a regular .py file), do the same but follow it with an implementation. The implementation should not be decorated with @overload."").
Т.е. по сути в предыдущем примере просто все складываем в один файл:
@overloaddef some_function(value: int) -> int: ...@overloaddef some_function(value: float) -> float: ...@overloaddef some_function(value: str) -> str: ...def some_function(value): if isinstance(value, int): retu value + 1 elif isinstance(value, float): retu value * 2.0 elif isinstance(value, str): retu ""Hello, "" + value else: raise TypeErrorТаким образом, @overload только декларирует разные возможные типы аргументов и возвращаемых значений одной функции, но не выполняет переключение между разными реализациями одной функции.
Это можно рассматривать как альтернативу Union, с помощью которого можно было бы типы описать так:
def some_function(value: Union[int, float, str]) -> Union[int, float, str]: ...Но с такими аннотациями не видно, что при полученном int возвращается int и т.д., а только декларируется, что может быть принят любой из перечисленных типов, и вернуться также любой из них (в любых сочетаниях).
:
from typing import overloadclass bytes: ... @overload def __getitem__(self, i: int) -> int: ... @overload def __getitem__(self, s: slice) -> bytes: ...Т.е. метод __getitem__ (обращение через квадратные скобки) у объекта bytes при передаче целого числа (индекса) вернет целое число, а при передаче слайса вернет набор байт.
singledispatch наоборот выполняет переключение на нужную реализацию в зависимости от типа аргумента:
from functools import singledispatch@singledispatchdef some_function(value): raise TypeError@some_function.registerdef _(value: int): retu value + 1@some_function.registerdef _(value: float): retu value * 2.0@some_function.registerdef _(value: str): retu ""Hello, "" + valueprint(some_function(2)) # 3print(some_function(2.0)) # 4.0print(some_function(""Insolor"")) # Hello, InsolorНа мой взгляд, более красиво (без подчеркиваний вместо имен функций) ""переключение"" между реализациями в зависимости от типа аргумента реализовано в библиотеке :
from fastcore.dispatch import typedispatch@typedispatchdef some_function(value): raise TypeError@typedispatchdef some_function(value: int): retu value + 1@typedispatchdef some_function(value: float): retu value * 2.0@typedispatchdef some_function(value: str): retu ""Hello, "" + valueprint(some_function(2)) # 3print(some_function(2.0)) # 4.0print(some_function(""Insolor"")) # Hello, Insolor