我正在使用 Python 3.10.9 和 PyCharm 2022.2.1(社区版)运行此测试。这是 PyCharm 的问题,而不是 Python 本身。
在我的示例代码中,我使用了一个生成器方法,该方法将类类型作为参数,并构造该类。我想确保传递的类类型是
BaseClass
的子类,所以我在 issubclass()
参数上使用 class_type
:
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
调用有一个警告,因为它没有在 bar
中看到 BaseClass
,即使它是一个 SubClass
对象由 Python 输出验证。
有人知道为什么会这样吗?对我来说这似乎是一个 PyCharm 错误,我不知道为什么简单地调用
issubclass
会让 PyCharm 假设对象是 BaseClass。如果不调用 issubclass
,PyCharm 可以毫无问题地理解它是 SubClass
的一个实例。
在此先感谢您的帮助。
我怀疑这不是真正的错误,而是不提供任何类型提示的副作用。 PyCharm 首先假定
class_type
作为类型 Any
,这意味着任何值都可以分配给名称,并且该值可以用于任何操作,无论类型如何。
那么 PyCharm 对
assert
语句做了什么?它尝试应用类型缩小,当断言为真时,为 class_type
分配比 Any
更具体的类型。作出断言 True
的最一般类型是 BaseClass
,因此在断言后面的代码中,它假设 class_type
的类型是 BaseClass
,而不是 Any
。 (如果断言失败,它不需要假设任何东西,因为下面的代码都不会执行。)
一旦类型缩小为
BaseClass
,其余的警告是不言自明的。 BaseClass
isn't 提供BaseClass.__init__
期望的参数,并且BaseClass
don't 的直接实例具有bar
属性。
在运行时,一切都很好,因为没有关于
class_type
类型的假设;它是SubClass
,因此代码按预期执行。
就是说,我没有安装 PyCharm 来测试。
mypy
什么都不做,这让我怀疑与 mypy
相比,PyCharm 在执行类型缩小方面过于激进。如果您提供明确的类型提示会发生什么,例如,
from typing import TypeVar
T = TypeVar('T', bound=BaseClass)
def generate_sub_class(class_type: type[T]) -> T:
assert issubclass(class_type, BaseClass)
new_obj = class_type()
return new_obj
在这里,类型提示提供了与
assert
语句可以推断出的相同数量的信息,但是由于类型提示已经暗示T
可以绑定到BaseClass
或BaseClass
的子类,所以不类型缩小应该是必要的或应用的。