我很好奇,是否有可能通过Python中的类型参数推断出通用类型(用于类型提示)。
例如,考虑一种(相当愚蠢的)工厂方法:
from typing import TypeVar, Type
T = TypeVar('T')
class Test1(object):
def test1(self):
pass
class Test2(object):
def test2(self):
pass
# Is the type hinting correct here?
def create( t: Type[T] ) -> T:
if t == Test1:
return Test1()
elif t == Test2:
return Test2()
else:
raise ValueError()
instance1 = create( Test1 )
instance2 = create( Test2 )
Visual Studio 2017和intellij Ultimate 2018似乎都没有在这里选择instance1和instance2的正确类型,至少在智能方面没有。 不过,我很好奇我的用法是否错误,或者IDE尚不支持这种用法。
谢谢
答案 0 :(得分:1)
您的类型签名非常好,您的IDE应该报告instance
和instance2
分别为Test1
和Test2
类型。实际上,这是如果您尝试针对mypy运行代码所获得的输出。 (要显示mypy认为您的类型是什么,请在运行mypy之前尝试将reveal_locals()
伪函数临时添加到代码底部。)
但是,您的代码的实际 body 实际上在技术上也不合理!您的代码的调用者可以传入一个自定义对象,该对象决定始终认为自己等于所有事物。例如:
from typing import TypeVar, Type
class ForceAlwaysEqual(type):
def __new__(cls, name, bases, dct):
return super().__new__(cls, name, bases, dct)
def __eq__(self, other) -> bool:
return True
class Hijacked(metaclass=ForceAlwaysEqual): pass
class Test1: pass
class Test2: pass
T = TypeVar('T')
def create(t: Type[T]) -> T:
if t == Test1:
return Test1()
elif t == Test2:
return Test2()
else:
raise ValueError()
mystery = create(Hijacked)
print(type(mystery))
如果您尝试在Python中运行此代码,我们将在最后打印出Test1
:对create
的调用不会引发ValueError!
这里基本上发生的是,我向Hijacked
添加了一个自定义元类,因此执行Hijacked == blah
总是会返回True。这会导致create(t == Test1
)内部的第一笔检查恰好是真的,因此我们返回Test1,这违反了您函数的签名。
因此,如果您的IDE也没有指示create
函数的 body 出现任何问题,那么从技术上讲,这实际上是他们的又一个错误。
(这里最普遍的教训是,在Python中,类型检查器很难从相等性检查中推断出很多东西,因为它们可以随时被重新定义以执行任意数量的胡说八道。)