I'm writing a very thin wrapper on top of list
and I want to define a method called reduce
, but I'm struggling to annotate it properly such that pylance
, mypy
& pylint
cut their complaints whenever I use the method, or even define it.
我在list的顶部写了一个非常薄的包装器,我想定义一个叫做reduce的方法,但是我很难正确地注释它,这样每当我使用这个方法,甚至定义它时,pylance,mypy和pylint都会减少他们的抱怨。
I was perturbed to realize that almost None of Python's builtin libraries are type annotated.
我很不安地意识到,几乎没有一个Python的内置库是带类型注释的。
This is my implementation attempt:
这是我的实现尝试:
def reduce(self, func: Callable[[list[T], list[T]], list[T]] = lambda x, y: x + y, default: Optional[T] = None) -> 'List[T]': # type: ignore
from functools import reduce
if default is None: return List(reduce(func, self.list)) # type: ignore
return List(reduce(func, self.list, default)) # type: ignore
This fails when my List
is actually a list of strings
当我的List实际上是一个字符串列表时,
a: List[str] = List(['a', 'b'])
b = a.reduce(lambda x, y: x + y)
Obviously here, the type checkers and linters say they expect list[T]
while I passed str
.
显然,在这里,当我传递str时,类型检查器和链接器说他们期望List[T]。
更多回答
优秀答案推荐
I think in this situation one would use a generic type, otherwise it can't reason about your types fully. Heres an example of a list using a Generic type which supports a Protocol that allows it to be reduce
d:
我认为在这种情况下,人们会使用泛型类型,否则它不能完全推理您的类型。下面是一个使用泛型类型的列表示例,该泛型类型支持允许减少列表的协议:
from functools import reduce
from typing import Sequence, TypeVar, Callable, Generic, Dict, Protocol, cast
A = TypeVar("A")
# define that our type supports the add operator, since it needs to be able to to reduce it
class SupportsAdd(Protocol[A]):
def __add__(self, __other: A) -> A:
...
T = TypeVar("T", bound=SupportsAdd)
def _reduce_default(x: T, y: T) -> T:
return x + y
# instead of using None as the default, we use a special object
# so that we can distinguish between None and a missing default
#
# (the underlying reduce function does the same thing)
_missing_default = object()
class MyList(Generic[T]):
def __init__(self, initialdata: Sequence[T]) -> None:
self.data = list(initialdata)
def __getitem__(self, i: int) -> T:
return self.data[i]
def __setitem__(self, i: int, v: T) -> None:
self.data[i] = v
def __len__(self) -> int:
return len(self.data)
def __delitem__(self, i: int) -> None:
del self.data[i]
def reduce(
self,
func: Callable[[T, T], T] = _reduce_default,
default: T = cast(T, _missing_default),
) -> T:
if default is _missing_default:
return reduce(func, self.data)
else:
return reduce(func, self.data, default)
# just another method that uses the types
def add_keys(self, keys: Dict[T, int]) -> None:
for k in keys:
self.data.append(k)
Some usage of it:
它的一些用法如下:
def test() -> None:
ll: MyList[int] = MyList([1, 2, 3])
print(ll[1])
ll[1] = 5
print(ll[1])
# add some keys
ll.add_keys({30: 1, 50: 2, 60: 3})
print(len(ll))
val: int # define the type separately, mypy warns us if it doesnt match
val = ll.reduce()
print(val)
# using strings instead
ll2: MyList[str] = MyList(["a", "b", "c"])
print(ll2.reduce())
2
5
6
149
abc
If you tried to use some class which didn't support __add__
, mypy
would warn you:
如果您尝试使用一些不支持__ADD__的类,mypy会警告您:
# test class to see if classes without __add__ fail
class NoAdd:
def __init__(self, v: int) -> None:
self.v = v
x: MyList[NoAdd] = [NoAdd(1)]
test.py:100: error: Type argument "NoAdd" of "MyList" must be a subtype of "SupportsAdd[Any]" [type-var]
test.py:100: error: Value of type variable "T" of "MyList" cannot be "NoAdd" [type-var]
Found 2 errors in 1 file (checked 1 source file)
更多回答
我是一名优秀的程序员,十分优秀!