如何使用带有可选第一个参数的键入提示
How to use typing hints with an optional first parameter
简介
我有一个名为 fun(start, stop, divisors)
或 fun(stop, divisors)
的函数。
- 我想按这个特定顺序调用参数。
- 我想以不产生任何类型提示错误的方式实现它。
如果我放宽这两个限制中的任何一个,实施起来就很容易。例如,放宽对订单的限制可以这样做:
def fun(stop: int, divisors: List[int], start: int=0) -> int:
...
return 0
fun(5, [1, 2, 3])
fun(5, [1, 2, 3], start=2)
或在下面的文件中显示了类型提示的缓动,但参数的顺序正确。
问题
我应该如何编写代码以使我的函数参数为:
- 静态类型;
- 特定顺序:
(start[optional], stop, divisors)
;
- 给出 0 个 mypy 错误?
尝试次数
import typing
from typing import Union, Optional
Start = int
Stop = int
Divisor = int
Divisors = list[Divisor]
@typing.overload
def fun1(x: Start, y: Stop, divisors: Divisors) -> int:
...
@typing.overload
def fun1(y: Stop, divisors: Divisors) -> int:
...
def fun1(*args) -> int:
if (arglen := len(args)) not in [2, 3]:
raise TypeError("Function expected 2 or 3 arguments, got", arglen)
if arglen == 2:
args = [0] + list(args)
start, stop, divisors = args
return 0
def fun2(x, y, divisors) -> int:
if divisors is None:
start, stop, divisors = 0, x, y
else:
start, stop = x, y
return 0
def fun3(*args) -> int:
if (arglen := len(args)) == 3:
start: Stop = args[0]
stop: int = args[1]
divisors: Divisors = args[2]
elif arglen == 2:
start: Stop = 0
stop: int = args[0]
divisors: Divisors = args[1]
else:
raise TypeError(
f"Too {'few' if arglen == 0 else 'many'} values to unpack (2-3), got",
arglen,
)
return 0
def fun4(*args) -> int:
if (arglen := len(args)) == 3:
pass
elif arglen == 2:
args = [0] + list(args)
else:
raise TypeError(
f"Too {'few' if arglen == 0 else 'many'} values to unpack (2-3), got",
arglen,
)
start: Stop = args[0]
stop: int = args[1]
divisors: Divisors = args[2]
return 0
typing_test_PE_001.py:20: error: Overloaded function implementation does not accept all possible arguments of signature 1
typing_test_PE_001.py:20: error: Overloaded function implementation does not accept all possible arguments of signature 2
typing_test_PE_001.py:24: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Tuple[Any, ...]")
typing_test_PE_001.py:43: error: Name "start" already defined on line 39
typing_test_PE_001.py:44: error: Name "stop" already defined on line 40
typing_test_PE_001.py:45: error: Name "divisors" already defined on line 41
typing_test_PE_001.py:58: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Tuple[Any, ...]")
Found 7 errors in 1 file (checked 1 source file)
让我们看看如何slice
is hinted:
class slice(object):
start: Any
step: Any
stop: Any
@overload
def __init__(self, stop: Any) -> None: ...
@overload
def __init__(self, start: Any, stop: Any, step: Any = ...) -> None: ...
__hash__: None # type: ignore
def indices(self, len: SupportsIndex) -> Tuple[int, int, int]: ...
class range(Sequence[int]):
start: int
stop: int
step: int
@overload
def __init__(self, stop: SupportsIndex) -> None: ...
@overload
def __init__(self, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex = ...) -> None: ...
[...]
基本上,您分别提示了两个重载版本,一个带有可选的第一个参数,一个没有。 (基本上,你的尝试 #2。)
from typing import overload, List
@overload
def fun(start: int, stop: int, divisors: List[int]):
...
@overload
def fun(stop: int, divisors: List[int]):
...
def fun(start, stop, divisors=None):
if divisors is None:
divisors = stop
stop = start
start = 0
...
fun(1, 2, [1,2,3]) # OK
fun(2, [1,2,3]) # OK
如果愿意,您也可以让两个变体都只接受位置参数:
# e.g.
def fun(start, stop, divisors=None, /):
...
简介
我有一个名为 fun(start, stop, divisors)
或 fun(stop, divisors)
的函数。
- 我想按这个特定顺序调用参数。
- 我想以不产生任何类型提示错误的方式实现它。
如果我放宽这两个限制中的任何一个,实施起来就很容易。例如,放宽对订单的限制可以这样做:
def fun(stop: int, divisors: List[int], start: int=0) -> int:
...
return 0
fun(5, [1, 2, 3])
fun(5, [1, 2, 3], start=2)
或在下面的文件中显示了类型提示的缓动,但参数的顺序正确。
问题
我应该如何编写代码以使我的函数参数为:
- 静态类型;
- 特定顺序:
(start[optional], stop, divisors)
; - 给出 0 个 mypy 错误?
尝试次数
import typing
from typing import Union, Optional
Start = int
Stop = int
Divisor = int
Divisors = list[Divisor]
@typing.overload
def fun1(x: Start, y: Stop, divisors: Divisors) -> int:
...
@typing.overload
def fun1(y: Stop, divisors: Divisors) -> int:
...
def fun1(*args) -> int:
if (arglen := len(args)) not in [2, 3]:
raise TypeError("Function expected 2 or 3 arguments, got", arglen)
if arglen == 2:
args = [0] + list(args)
start, stop, divisors = args
return 0
def fun2(x, y, divisors) -> int:
if divisors is None:
start, stop, divisors = 0, x, y
else:
start, stop = x, y
return 0
def fun3(*args) -> int:
if (arglen := len(args)) == 3:
start: Stop = args[0]
stop: int = args[1]
divisors: Divisors = args[2]
elif arglen == 2:
start: Stop = 0
stop: int = args[0]
divisors: Divisors = args[1]
else:
raise TypeError(
f"Too {'few' if arglen == 0 else 'many'} values to unpack (2-3), got",
arglen,
)
return 0
def fun4(*args) -> int:
if (arglen := len(args)) == 3:
pass
elif arglen == 2:
args = [0] + list(args)
else:
raise TypeError(
f"Too {'few' if arglen == 0 else 'many'} values to unpack (2-3), got",
arglen,
)
start: Stop = args[0]
stop: int = args[1]
divisors: Divisors = args[2]
return 0
typing_test_PE_001.py:20: error: Overloaded function implementation does not accept all possible arguments of signature 1
typing_test_PE_001.py:20: error: Overloaded function implementation does not accept all possible arguments of signature 2
typing_test_PE_001.py:24: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Tuple[Any, ...]")
typing_test_PE_001.py:43: error: Name "start" already defined on line 39
typing_test_PE_001.py:44: error: Name "stop" already defined on line 40
typing_test_PE_001.py:45: error: Name "divisors" already defined on line 41
typing_test_PE_001.py:58: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Tuple[Any, ...]")
Found 7 errors in 1 file (checked 1 source file)
让我们看看如何slice
is hinted:
class slice(object):
start: Any
step: Any
stop: Any
@overload
def __init__(self, stop: Any) -> None: ...
@overload
def __init__(self, start: Any, stop: Any, step: Any = ...) -> None: ...
__hash__: None # type: ignore
def indices(self, len: SupportsIndex) -> Tuple[int, int, int]: ...
class range(Sequence[int]):
start: int
stop: int
step: int
@overload
def __init__(self, stop: SupportsIndex) -> None: ...
@overload
def __init__(self, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex = ...) -> None: ...
[...]
基本上,您分别提示了两个重载版本,一个带有可选的第一个参数,一个没有。 (基本上,你的尝试 #2。)
from typing import overload, List
@overload
def fun(start: int, stop: int, divisors: List[int]):
...
@overload
def fun(stop: int, divisors: List[int]):
...
def fun(start, stop, divisors=None):
if divisors is None:
divisors = stop
stop = start
start = 0
...
fun(1, 2, [1,2,3]) # OK
fun(2, [1,2,3]) # OK
如果愿意,您也可以让两个变体都只接受位置参数:
# e.g.
def fun(start, stop, divisors=None, /):
...