你的发现让我着迷,因为在 Python(以及我知道的所有其他语言)中确实是非法的,具有领先的可选参数,这在我们的案例中肯定会出现:
SyntaxError: non-default argument follows default argument
我很怀疑,但我已经搜索了源代码:
我发现,TensorFactories.cpp 的第 566-596 行实际上有几个 (!) 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);
}
这种模式在gen_pyi.py 的第 466-471 行再次出现,它为顶级函数生成类型签名:
'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,我们看到这些函数被收集到在第436行定义的unsorted_function_hints,然后在第509-513行用于创建function_hints,最后将function_hints设置为@987654340 @ 在第 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,但参数个数不同。