在下面的代码中,
print_pos
接受一个参数,该参数可以是
三种不同的类型。
from typing import List, Tuple, Union
pos_t = Tuple[int, int]
anchor_t = Tuple[str, str]
anchor_pos_t = Tuple[anchor_t, pos_t]
def print_pos(
pos: Union[
pos_t,
anchor_pos_t,
List[Union[pos_t, anchor_pos_t]]
]
) -> None:
if isinstance(pos, tuple) and isinstance(pos[0], int):
print('xy =', pos)
elif isinstance(pos, tuple) and isinstance(pos[0], tuple):
print('anchor =', pos[0])
print('xy =', pos[1])
elif isinstance(pos, list):
print('[')
for p in pos:
print_pos(p)
print(']')
else:
raise ValueError('invalid pos')
print_pos((0, 100))
print_pos((('right', 'bottom'), (0, 100)))
print_pos([
(0, 100),
(('right', 'bottom'), (0, 100))
])
现在,我使用
isinstance
来检查不同的可能性
对于 pos
的类型,但我发现代码相当笨拙。 有没有
更方便/优雅的方式来做到这一点? 特别是有一个意思
重用我在类型检查中定义的类型(pos_t
、anchor_t
、anchor_pos_t
)?
typeguard
库在运行时检查变量类型。
这个库主要用于运行时类型验证,而不是条件类型检查,因此需要定义一个额外的
is_type
函数来满足您的需求。
不幸的是,为了防止类型检查器错误,还需要额外的类型转换。
from typing import Any, List, Tuple, Union, cast
from typeguard import check_type
pos_t = Tuple[int, int]
anchor_t = Tuple[str, str]
anchor_pos_t = Tuple[anchor_t, pos_t]
def is_type(value: Any, expected_type: Any) -> bool:
"""
Return whether the given value is of the expected type or not.
"""
try:
check_type('<blank>', value, expected_type)
return True
except TypeError:
return False
def print_pos(
pos: Union[pos_t, anchor_pos_t, List[Union[pos_t, anchor_pos_t]]]
) -> None:
if is_type(pos, pos_t):
pos = cast(pos_t, pos)
print('xy =', pos)
elif is_type(pos, anchor_pos_t):
pos = cast(anchor_pos_t, pos)
print('anchor =', pos[0])
print('xy =', pos[1])
elif is_type(pos, List[Union[pos_t, anchor_pos_t]]):
pos = cast(List[Union[pos_t, anchor_pos_t]], pos)
print('[')
for p in pos:
print_pos(p)
print(']')
else:
raise ValueError('invalid pos')
print_pos((0, 100))
print_pos((('right', 'bottom'), (0, 100)))
print_pos([(0, 100), (('right', 'bottom'), (0, 100))])
这不是最干净的解决方案,但它确实有效。
如果可能的话,我建议对类使用更加面向对象的方法,以消除对联合类型的需要。
这尚不适用于任何当前的 python 版本,但 python 3.10(计划于 2021 年 10 月发布)将具有结构模式匹配。
这允许类似这样的事情,这可能会稍微更具可读性:
from typing import List, Tuple, Union
pos_t = Tuple[int, int]
anchor_t = Tuple[str, str]
anchor_pos_t = Tuple[anchor_t, pos_t]
def print_pos(
pos: Union[
pos_t,
anchor_pos_t,
List[Union[pos_t, anchor_pos_t]]
]
) -> None:
match pos:
# need to match this more specific case first, as tuple((x, y)) matches this as well
case tuple(((a, b), (x, y))):
print('anchor =', (a, b))
print('xy =', (x, y))
case tuple((x, y)):
print('xy =', (x, y))
case list(_):
print('[')
for p in pos:
print_pos(p)
print(']')
case _:
raise ValueError('invalid pos')
print_pos((0, 100))
print_pos((('right', 'bottom'), (0, 100)))
print_pos([
(0, 100),
(('right', 'bottom'), (0, 100))
])
这已经适用于 3.10.0a7 预发布版,尽管尚未提供 mypy 支持。
结构模式匹配在某种意义上类似于序列拆包
(a, b), (x, y) = pos
但更强大。
描述结构模式匹配的三个 PEP: