如何区分两种动态类型?
How can I distinguish between two dynamic types?
我正在对一个使用 pandas 的项目进行类型检查。 Pandas 不包含类型注释并且类型流中没有存根文件。
如我所料,mypy 为这个简单示例引发错误:
class A:
def method(self) -> int:
return 1
class B(A):
def method(self) -> float:
return 1.1
$ mypy mypy_example.py
mypy_example.py:11: error: Return type "float" of "method" incompatible with return type "int" in supertype "A"
考虑以下示例:
class C:
def method(self) -> pd.Series:
return pd.Series([1, 2, 3])
class D(C):
def method(self) -> pd.DataFrame:
return pd.DataFrame({"a": [1, 2, 3]})
不出所料,mypy 说找不到 pandas 的存根文件,所以它没有找到错误。
$ mypy mypy_example.py
mypy_example.py:1: error: No library stub file for module 'pandas'
mypy_example.py:1: note: (Stub files are from https://github.com/python/typeshed)
mypy_example.py:11: error: Return type "float" of "method" incompatible with return type "int" in supertype "A"
我可以设置 ignore_missing_imports
,但这意味着我错过了我想要捕获的错误。
我在存根文件中尝试了一些东西但没有成功:
from typing import Any, NewType
# dynamic typing, but doesn't discriminate between Series and DataFrame
Series = Any
DataFrame = Any
# discriminates but doesn't dynamically type
Series = NewType('Series', object)
DataFrame = NewType('DataFrame', object)
是否可以编写一个简短的存根文件或类型注释,使我能够利用动态类型但认识到 pd.Series
和 pd.DataFrame
是不同的类型?
而不是试图让 mypy 区分两个动态 classes,我实际上会采取使它们 not 动态(或者更确切地说,仅部分 动态)通过将它们定义为完全成熟的 classes 在你的存根中。
您可以通过将您的两个 class 定义为如下所示,从一组非常初步和最小的存根开始:
from typing import Any
class Series:
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
class DataFrame(Series):
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
__getattr__
函数让 mypy 明白你的 class 是不完整的,没有完全注释。这意味着像 DataFrame().query(...)
这样的事情将继续进行类型检查,即使该功能从未明确添加到您的 class.
当然,如果您确实决定添加一些方法签名,mypy 将开始对这些调用进行类型检查,而不是让它们动态键入。这意味着您还可以根据需要逐步添加更精确的方法签名,并可能最终完全摆脱 __getattr__
。
如果您决定走这条路,您可能会发现现有的 numpy stubs to be a good source of inspiration. And if you want really precise types, the discussion here 可能是相关的。
writing incomplete stubs 上的 typeshed 指南提供了有关编写部分存根的更多信息,如果您有兴趣的话。
我正在对一个使用 pandas 的项目进行类型检查。 Pandas 不包含类型注释并且类型流中没有存根文件。
如我所料,mypy 为这个简单示例引发错误:
class A:
def method(self) -> int:
return 1
class B(A):
def method(self) -> float:
return 1.1
$ mypy mypy_example.py
mypy_example.py:11: error: Return type "float" of "method" incompatible with return type "int" in supertype "A"
考虑以下示例:
class C:
def method(self) -> pd.Series:
return pd.Series([1, 2, 3])
class D(C):
def method(self) -> pd.DataFrame:
return pd.DataFrame({"a": [1, 2, 3]})
不出所料,mypy 说找不到 pandas 的存根文件,所以它没有找到错误。
$ mypy mypy_example.py
mypy_example.py:1: error: No library stub file for module 'pandas'
mypy_example.py:1: note: (Stub files are from https://github.com/python/typeshed)
mypy_example.py:11: error: Return type "float" of "method" incompatible with return type "int" in supertype "A"
我可以设置 ignore_missing_imports
,但这意味着我错过了我想要捕获的错误。
我在存根文件中尝试了一些东西但没有成功:
from typing import Any, NewType
# dynamic typing, but doesn't discriminate between Series and DataFrame
Series = Any
DataFrame = Any
# discriminates but doesn't dynamically type
Series = NewType('Series', object)
DataFrame = NewType('DataFrame', object)
是否可以编写一个简短的存根文件或类型注释,使我能够利用动态类型但认识到 pd.Series
和 pd.DataFrame
是不同的类型?
而不是试图让 mypy 区分两个动态 classes,我实际上会采取使它们 not 动态(或者更确切地说,仅部分 动态)通过将它们定义为完全成熟的 classes 在你的存根中。
您可以通过将您的两个 class 定义为如下所示,从一组非常初步和最小的存根开始:
from typing import Any
class Series:
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
class DataFrame(Series):
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
__getattr__
函数让 mypy 明白你的 class 是不完整的,没有完全注释。这意味着像 DataFrame().query(...)
这样的事情将继续进行类型检查,即使该功能从未明确添加到您的 class.
当然,如果您确实决定添加一些方法签名,mypy 将开始对这些调用进行类型检查,而不是让它们动态键入。这意味着您还可以根据需要逐步添加更精确的方法签名,并可能最终完全摆脱 __getattr__
。
如果您决定走这条路,您可能会发现现有的 numpy stubs to be a good source of inspiration. And if you want really precise types, the discussion here 可能是相关的。
writing incomplete stubs 上的 typeshed 指南提供了有关编写部分存根的更多信息,如果您有兴趣的话。