我用Python 3.10.9和PyCharm 2022.2.1(社区版)运行这个测试,这是PyCharm的问题,而不是Python本身。
在我的示例代码中,我使用了一个生成器方法,它接受一个类类型作为参数,并构造这个类。我想确保传递的类类型是BaseClass
的子类,所以我在class_type
参数上使用了issubclass()
:
class BaseClass:
def __init__(self, foo):
self.foo = foo
class SubClass(BaseClass):
def __init__(self):
super().__init__(foo='hi')
self.bar = "hello"
def generate_sub_class(class_type):
assert issubclass(class_type, BaseClass)
new_obj = class_type() # <--- PyCharm warning: "Parameter 'foo' unfilled"
return new_obj
# Main
new_subclass = generate_sub_class(SubClass)
print(f"Type of new_subclass: {type(new_subclass)}")
print(f"new_subclass.foo: {new_subclass.foo}")
print(f"new_subclass.bar: {new_subclass.bar}") # <--- PyCharm warning: "Unresolved attribute reference
# 'bar' for class 'BaseClass'"
当在Python中运行时,这段代码可以正常工作,我们可以得到以下输出:
Type of new_subclass: <class '__main__.SubClass'>
new_subclass.foo: hi
new_subclass.bar: hello
然而,正如你在上面的代码中看到的,只要我调用issubclass(class_type, BaseClass)
,PyCharm就会认为构造的对象是BaseClass
对象,构造函数调用会有一个警告,因为它正在查看错误的构造函数,Main中的new_subclass.bar
调用会有一个警告,因为它在BaseClass
中没有看到bar
。尽管Python输出验证了它是SubClass
对象。
有人知道为什么会发生这种情况吗?在我看来,这似乎是PyCharm的错误,我不知道为什么简单地调用issubclass
会使PyCharm假设对象是BaseClass。如果不调用issubclass
,PyCharm就可以理解它是SubClass
的示例。
提前感谢您的帮助。
2条答案
按热度按时间nnsrf1az1#
我怀疑这不是一个真正的bug,而是没有提供任何类型提示的副作用。PyCharm从
class_type
作为Any
类型的假设开始,这意味着可以为名称赋任何值,并且该值可以用于任何操作,而不管类型如何。那么PyCharm对
assert
语句做了什么呢?它尝试应用类型收缩,当Assert为真时,为class_type
分配一个比Any
更具体的类型。使AssertTrue
生效的最常见类型是BaseClass
,所以在Assert之后的代码中,它假设class_type
具有BaseClass
类型。而不是Any
。(如果Assert失败,则不需要进行任何假设,因为下面的代码都不会执行。)一旦类型被缩小到
BaseClass
,其余的警告就不言自明了。BaseClass
* 不 * 提供BaseClass.__init__
期望的参数,并且BaseClass
的直接示例 * 不 * 具有bar
属性。在运行时,一切正常,因为没有关于
class_type
类型的假设;它 * 是 *SubClass
,因此代码按预期执行。也就是说,我没有安装PyCharm进行测试。
mypy
什么也不做,这使我怀疑PyCharm在执行类型收缩方面比mypy
过于激进。这里,类型提示提供了可从
assert
语句推断出的相同量的信息,但由于类型提示已经暗示T
可被绑定到BaseClass
或BaseClass
的子类,因此不应需要或应用类型缩窄。qkf9rpyu2#
所以多亏了@chepner,抑制PyCharm警告并使PyCharm正确理解类型的最好方法是使用类型提示而不是Assert。
所有警告现在都消失了。