Ваше открытие очаровало меня, так как в Python (и во всех других языках, которые я знаю) действительно незаконно иметь ведущие необязательные аргументы, которые наверняка вызовут в нашем случае:
SyntaxError: non-default argument follows default argument
У меня возникли подозрения, но я искал исходный код:
Я нашел в строках 566-596 TensorFactories.cpp
что на самом деле существует несколько (!) реализаций randint
:
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ randint ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tensor randint(int64_t high, IntArrayRef size, const TensorOptions& options) {
return native::randint(high, size, c10::nullopt, options);
}
Tensor randint(
int64_t high,
IntArrayRef size,
c10::optional<Generator> generator,
const TensorOptions& options) {
return native::randint(0, high, size, generator, options);
}
Tensor randint(
int64_t low,
int64_t high,
IntArrayRef size,
const TensorOptions& options) {
return native::randint(low, high, size, c10::nullopt, options);
}
Tensor randint(
int64_t low,
int64_t high,
IntArrayRef size,
c10::optional<Generator> generator,
const TensorOptions& options) {
auto result = at::empty(size, options);
return result.random_(low, high, generator);
}
Этот шаблон повторяется в строках 466–471 документа gen_pyi.py
. где он генерирует сигнатуры типов для функций верхнего уровня:
'randint': ['def randint(low: _int, high: _int, size: _size, *,'
' generator: Optional[Generator]=None, {}) -> Tensor: ...'
.format(FACTORY_PARAMS),
'def randint(high: _int, size: _size, *,'
' generator: Optional[Generator]=None, {}) -> Tensor: ...'
.format(FACTORY_PARAMS)],
Итак, что в основном происходит, так это то, что нет реального опционального параметра, а есть несколько функций, в которых один присутствует, а в другом его нет.
Это означает, что когда randint
вызывается без параметра low
, он устанавливается как 0
:
Tensor randint(
int64_t high,
IntArrayRef size,
c10::optional<Generator> generator,
const TensorOptions& options) {
return native::randint(0, high, size, generator, options);
}
Дальнейшее исследование, что касается запроса OP о том, как это возможно, что существует несколько функций с одним и тем же именем и разными аргументами:
Возвращаясь еще раз к gen_pyi.py
, мы видим, что эти функции собираются в unsorted_function_hints
, определенный в строке 436, затем он используется для создания function_hints
в строках 509-513, и, наконец, function_hints
устанавливается в env
в строке 670.
Словарь env
используется для записи файлов-заглушек pyi
.
Эти файлы-заглушки используют перегрузку функций/методов, как описано в PEP-484.
Перегрузка функций/методов, используйте декоратор @overload
:
Декоратор @overload позволяет описывать функции и методы, поддерживающие различные комбинации типов аргументов. Этот шаблон часто используется во встроенных модулях и типах.
Вот пример:
from typing import overload
class bytes:
...
@overload
def __getitem__(self, i: int) -> int: ...
@overload
def __getitem__(self, s: slice) -> bytes: ...
Итак, у нас есть определение одной и той же функции __getitem__
с разными аргументами.
И еще пример:
from typing import Callable, Iterable, Iterator, Tuple, TypeVar, overload
T1 = TypeVar('T1')
T2 = TypeVar('T2')
S = TypeVar('S')
@overload
def map(func: Callable[[T1], S], iter1: Iterable[T1]) -> Iterator[S]: ...
@overload
def map(func: Callable[[T1, T2], S],
iter1: Iterable[T1], iter2: Iterable[T2]) -> Iterator[S]: ...
# ... and we could add more items to support more than two iterables
Здесь у нас есть определение той же функции map
с другим количеством аргументов.
person
Aviv Yaniv
schedule
09.09.2020
\*
не является допустимым спецификатором подписи. Вы имели в виду только\
или*
? - person MisterMiyagi   schedule 09.09.2020\*
! Я только что скопировал из документацииtorch.randint
. Как вы упомянули, мне очень любопытна его реализация в Python. - person javadr   schedule 09.09.2020