在pycharm中对类修饰属性进行类型检查

7uzetpgm  于 5个月前  发布在  PyCharm
关注(0)|答案(2)|浏览(60)

我使用PyCharm 2023.2.3(Community Edition)进行开发。有一个脚本:

from functools import cached_property
from collections.abc import Callable

from typing import TypeVar, Generic, Any, overload, Union

T = TypeVar("T")

class result_property(cached_property, Generic[T]):
    def __init__(self, func: Callable[[Any], T]) -> None:
        super().__init__(func)

    def __set_name__(self, owner: type[Any], name: str) -> None:
        super().__set_name__(owner, name)

    @overload
    def __get__(self, instance: None, owner: Union[type[Any], None] = None) -> 'result_property[T]': ...

    @overload
    def __get__(self, instance: object, owner: Union[type[Any], None] = None) -> T: ...

    def __get__(self, instance, owner=None):
        return super().__get__(instance, owner)

def func_str(s: str) -> None:
    print(s)

class Foo:
    @result_property
    def prop_int(self) -> int:
        return 1

foo = Foo()
func_str(foo.prop_int)  # type error is expected here

字符串
PyCharm类型检查器报告它是正确的。然而,我除了它在最后一行报告一个类型错误。如果我在这个脚本上运行mypy检查,它报告:

tmp.py:38: error: Argument 1 to "func_str" has incompatible type "int"; expected "str"  [arg-type]
Found 1 error in 1 file (checked 1 source file)


为什么Pycharm报告它没问题?
有没有可能以某种方式修改PyCharm的脚本,以便在这种情况下也报告类型错误?

pcww981p

pcww981p1#

显然,PyCharm在其静态类型检查器中硬编码了名称cached_property的逻辑,而不是通过适当的类型推断系统来推断修饰方法的类型。它可以是完全虚假的,只要它名为cached_property,PyCharm仍然会使用相同的逻辑:

def cached_property(func):
    def foo(self):
        pass
    return foo

def func_str(s: str) -> None:
    print(s)

class Foo:
    @cached_property
    def prop_int(self) -> int:
        return 1

foo = Foo()
func_str(foo.prop_int) # PyCharm complains: Expected type 'str', got 'int' instead

字符串
因此,为了让你自己的描述符像cached_property一样被处理,你只需将其命名为cached_property,以便PyCharm进行类型检查:

import functools

class cached_property(functools.cached_property, Generic[T]):
    ... # definition omitted

def func_str(s: str) -> None:
    print(s)

class Foo:
    @cached_property
    def prop_int(self) -> int:
        return 1

foo = Foo()
func_str(foo.prop_int) # PyCharm complains: Expected type 'str', got 'int' instead


这并不理想,但必须这样做,直到PyCharm停止基于硬编码名称硬编码类型逻辑的捷径,并为描述符实现逻辑推断类型。

fae0ux8s

fae0ux8s2#

我刚刚用PyCharm 2023.2.5检查了一下,没有发现任何错误,即使是在尝试更复杂的类版本时,比如在result_property类中增强类型提示以提供PyCharm更多信息:

from functools import cached_property
from collections.abc import Callable
from typing import TypeVar, Generic, Any, Union, cast

T = TypeVar("T")

class result_property(cached_property, Generic[T]):
    def __init__(self, func: Callable[[Any], T]) -> None:
        super().__init__(func)

    def __set_name__(self, owner: type[Any], name: str) -> None:
        super().__set_name__(owner, name)

    def __get__(self, instance: Any, owner: Union[type[Any], None] = None) -> Union[T, 'result_property[T]']:
        if instance is None:
            return self
        result = super().__get__(instance, owner)
        return cast(T, result)

def func_str(s: str) -> None:
    print(s)

class Foo:
    @result_property
    def prop_int(self) -> int:
        return 1

foo = Foo()
func_str(foo.prop_int)  # Hoping PyCharm reports a type error here

字符串
考虑到PyCharm即使在增强了类型提示之后仍然没有报告类型错误,我认为PyCharm的类型推断系统在处理像您这样的复杂场景时有局限性或行为不同,涉及自定义装饰器和泛型。PyCharm和mypy之间的差异并不罕见,特别是在更复杂或细微的类型情况下。
所以,你的选择,在这一点上,是:

  • 使用mypy或其他静态类型检查器作为开发工作流的一部分。在测试或构建过程中单独运行mypy可以帮助捕获这些类型的错误,即使PyCharm没有。

你可以install it in PyCharm

作为一种(非理想的)解决方法,如果类型检查问题对您的项目很重要,请考虑调整代码结构。有时,简化代码或避免过于复杂的类型构造可以使其与各种类型检查器更兼容。

相关问题