我有一个类型别名,我称之为
point2d
和一个相关的递归类型别名。
point2d = tuple[float, float]
point2d_nested = (
point2d
| list["point2d_nested"]
| tuple["point2d_nested", ...]
)
现在我想编写一个函数,可以按一个因子缩放任何嵌套结构中的点。例如,
scale_nested((1,1), 2) == (2,2)
或 scale_nested([(1,1), (1,1)]) == [(2,2), (2,2)]
。这是一个实现:
def scale_nested(points: point2d_nested, factor: float) -> point2d_nested:
"""
Scales a list of lists of points by a given factor.
Weird things happen if you supply 3D points or if your point is not a tuple.
"""
if not points:
return points
# We've bottomed out at a point
if (
isinstance(points, tuple)
and len(points) == 2
and isinstance(points[0], (int, float))
and isinstance(points[1], (int, float))
):
return points[0] * factor, points[1] * factor
if isinstance(points, list):
return [scale_nested(p, factor) for p in points]
if isinstance(points, tuple):
return tuple(
scale_nested(p, factor) for p in points if not isinstance(p, (int, float))
)
raise ValueError(f"Something is an invalid type: {points}")
不幸的是,这不起作用:
knots = [((1.0, 2.0),)]
scale_nested(knots, 2) # <- knots look red
Argument of type "list[tuple[tuple[float, float]]]" cannot be assigned to parameter "points" of type "point2d_nested" in function "scale_nested"
Type "list[tuple[tuple[float, float]]]" cannot be assigned to type "point2d_nested"
"list[tuple[tuple[float, float]]]" is incompatible with "point2d"
"list[tuple[tuple[float, float]]]" is incompatible with "list[point2d_nested]"
Type parameter "_T@list" is invariant, but "tuple[tuple[float, float]]" is not the same as "point2d_nested"
Consider switching from "list" to "Sequence" which is covariant
"list[tuple[tuple[float, float]]]" is incompatible with "tuple[point2d_nested, ...]"PylancereportGeneralTypeIssues
(variable) knots: list[tuple[tuple[float, float]]]
根据 Pyright/Pylance 的建议,您的代码将按原样使用
Sequence
而不是 list
:
(游乐场链接)
from collections.abc import Sequence
point2d_nested = (
point2d
| Sequence["point2d_nested"]
| tuple["point2d_nested", ...]
)
knots = [((1.0, 2.0),)] # list[tuple[tuple[float, float]]]
scale_nested(knots, 2) # fine
但是,请注意
scale_nested(knots, 2)
的类型。显示为:
tuple[float, float] | Sequence[point2d_nested] | tuple[point2d_nested, ...]
...也称为
point2d_nested
。
但是,我们可以做得更好。使用
TypeVar
告诉类型检查器,返回类型将与输入类型完全相同,只要它与 point2d_nested
兼容即可:
(游乐场链接)
T = TypeVar('T', bound = point2d_nested)
def scale_nested(points: T, factor: float) -> T:
...
knots_1 = [((1.0, 2.0),)]
reveal_type(scale_nested(knots_1, 2)) # list[tuple[tuple[float, float]]]
knots_2 = ((1.2, 3.4), (5.6, 7.8))
reveal_type(scale_nested(knots_2, 2)) # tuple[tuple[float, float], tuple[float, float]]
knots_3 = ([(1.2, 3.4)], [(5.6, 7.8), [(9.1, 2.3)]])
reveal_type(scale_nested(knots_3, 2)) # tuple[list[tuple[float, float]], list[tuple[float, float] | list[tuple[float, float]]]]
knots_4 = [('foo', 'bar')]
reveal_type(scale_nested(knots_4, 2)) # error
knots_5 = ([(1.2, 3.4)], [(5.6, 7.8), [9.1, 2.3]])
reveal_type(scale_nested(knots_5, 2)) # error