为什么 Pylance 对带有类型提示的一个函数显示错误,而对另一个函数则不显示错误?

问题描述 投票:0回答:2

经过严格检查的 VSCode Pylance v2023.9.20 在第一个函数调用时给我类型错误,但在第二个函数调用时却没有。唯一的区别是第一个参数包含在列表中。

def listTupleFunc(arg: list[tuple[str, tuple[str, ...]]]) -> None:
    pass
listTuple = [('x', ('a', 'b'))]
listTupleFunc(listTuple) # Error here

def tupleOnlyFunc(arg: tuple[str, tuple[str, ...]]) -> None:
    pass
tupleOnly = ('x', ('a', 'b'))
tupleOnlyFunc(tupleOnly) # OK

错误信息是:

Argument of type "list[tuple[Literal['x'], tuple[Literal['a'], Literal['b']]]]" cannot be assigned to parameter "arg" of type "list[tuple[str, tuple[str, ...]]]" in function "listTupleFunc"
  "list[tuple[Literal['x'], tuple[Literal['a'], Literal['b']]]]" is incompatible with "list[tuple[str, tuple[str, ...]]]"
    Type parameter "_T@list" is invariant, but "tuple[Literal['x'], tuple[Literal['a'], Literal['b']]]" is not the same as "tuple[str, tuple[str, ...]]"

如果我进行更改,以便对文字的some进行格式化,令人惊讶的是,错误消失了:

# These work OK somehow
listTuple = [(f'x', ('a', f'b'))]  
listTuple = [(f'x', (f'a', 'b'))]  
# But these don't
listTuple = [('x', (f'a', f'b'))]
listTuple = [(f'x', ('a', 'b'))]

这是 Pylance 中的错误还是我遗漏了一些明显的东西?如何消除错误?

python visual-studio-code type-hinting pylance
2个回答
1
投票

您可以将

listTuple
转换为所需类型。

def listTupleFunc(arg: list[tuple[str, tuple[str, ...]]]) -> None:
    pass
listTuple: list[tuple[str, tuple[str, ...]]] = [('x', ('a', 'b'))]
listTupleFunc(listTuple) # No error!

def tupleOnlyFunc(arg: tuple[str, tuple[str, ...]]) -> None:
    pass
tupleOnly = ('x', ('a', 'b'))
tupleOnlyFunc(tupleOnly) # OK

这将消除错误。 PyLance 预计这些硬编码文字与您使用的一般定义不相容。


0
投票

因此,错误消息非常具有描述性。当你这样做时:

listTuple = [('x', ('a', 'b'))]

然后pyright infer的类型推断规则:

list[tuple[Literal['x'], tuple[Literal['a'], Literal['b']]]]

现在,您可能会说,“但是

tuple[Literal['x'], tuple[Literal['a'], Literal['b']]
tuple[str, tuple[str, ...]]
的子类型,这里应该接受它!

但是看看错误消息的其余部分,它指出,

list
不变。因此,如果您有
list[T]
,并且
list[S]
并且
S
T
的子类型,那么
list[S]
并不是
list[T]
的子类型,正如您所期望的

人们倾向于期望参数化类型是协变

list
对象必须保持不变,因为它们是可变的(在 PEP 484 中了解更多相关信息)。

考虑以下玩具示例:

class T:
    pass

class S(T):
    pass

s = S() # inferring type S
reveal_type(s)

t: T = s # OK!

data_s = [S(), S()] # inferring type list[s]
reveal_type(data_s)

data_t: list[T] =  data_s # Whoops!

这是

pyright
在我的机器上显示的内容:

jarrivillaga-mbp16-2019:scratch jarrivillaga$ pyright test_typing.py 
No configuration file found.
No pyproject.toml file found.
stubPath /Users/jarrivillaga/scratch/typings is not a valid directory.
Assuming Python platform Darwin
Searching for source files
Found 1 source file
/Users/jarrivillaga/scratch/test_typing.py
  /Users/jarrivillaga/scratch/test_typing.py:8:13 - info: Type of "s" is "S"
  /Users/jarrivillaga/scratch/test_typing.py:13:13 - info: Type of "data_s" is "list[S]"
  /Users/jarrivillaga/scratch/test_typing.py:15:20 - error: Expression of type "list[S]" cannot be assigned to declared type "list[T]"
    TypeVar "_T@list" is invariant
      "S" is incompatible with "T" (reportGeneralTypeIssues)
1 error, 0 warnings, 2 infos 
Completed in 0.495sec

因此,一种解决方案是使用超类型显式注释变量,并且不依赖类型推断,在玩具示例中:

data_s: list[T] = [S(), S()] # don't rely on inference!

或者您的代码:

listTuple: tuple[str, tuple[str, ...]] = [('x', ('a', 'b'))]

另一种选择,如果您不需要依赖任何 mutator 方法来实现

list
(例如
.append
.pop
mlist[i] = whatever
),那么您可以使用
collections.abc.Sequence
,即 协变

from collections.abc import Sequence

class T:
    pass

class S(T):
    pass

s = S() # inferring type S
reveal_type(s)

t: T = s # OK!

data_s = [S(), S()] # inferring type list[s]
reveal_type(data_s)

data_t: Sequence[T] =  data_s # covariance, yay!

现在请注意:

jarrivillaga-mbp16-2019:scratch jarrivillaga$ pyright test_typing.py 
No configuration file found.
No pyproject.toml file found.
stubPath /Users/jarrivillaga/scratch/typings is not a valid directory.
Assuming Python platform Darwin
Searching for source files
Found 1 source file
/Users/jarrivillaga/scratch/test_typing.py
  /Users/jarrivillaga/scratch/test_typing.py:10:13 - info: Type of "s" is "S"
  /Users/jarrivillaga/scratch/test_typing.py:15:13 - info: Type of "data_s" is "list[S]"
0 errors, 0 warnings, 2 infos 
Completed in 0.489sec
© www.soinside.com 2019 - 2024. All rights reserved.