为第三方扩展存根文件 library/module
Extending stub file for a third-party library/module
我正在使用 yarl
库的 URL
对象。
它有一个准私有属性,._val
,它是一个 urllib.parse.SplitResult
对象,但在 yarl/__init__.pyi
中没有类型注释。 (可以理解,如果开发者不想正式将其作为 public API 的一部分。)
但是,我选择使用 URL._val
风险自负。一个虚拟的例子:
# urltest.py
from urllib.parse import SplitResult
from typing import Tuple
from yarl import URL
def foo(u: URL) -> Tuple[str, str, str]:
sr: SplitResult = u._val
return sr[:3]
但是mypy
不喜欢这样,因为它抱怨:
$ mypy urltest.py
"URL" has no attribute "_val"
那么,在我自己的项目中,我如何才能 "tack on"(或扩展)实例属性注释到 URL
,以便它可以在我的项目的其余部分中使用?即
from yarl import URL
URL._val: SplitResult
# ...
(mypy也不喜欢这样;"Type cannot be declared in assignment to non-self attribute.")
更新
我尝试在 stubs/yarl/__init__.pyi
:
中创建一个新的存根文件
from urllib.parse import SplitResult
class URL:
_val: SplitResult
然后按照stub files中的描述设置export MYPYPATH='.../stubs'
。但是,此 覆盖,而不是扩展, 现有注释,因此 但 ._val
的所有内容都会抛出错误:
error: "URL" has no attribute "with_scheme"
error: "URL" has no attribute "host"
error: "URL" has no attribute "fragment"
...等等。
一种可能性是简单地忽略此赋值的 u
类型:
def foo(u: URL) -> Tuple[str, str, str, str]:
sr: SplitResult = typing.cast(typing.Any, u)._val
return sr[:3]
mypy
将假定您知道自己在做什么,并且 u
具有类型为 str
.
的 _val
属性
不幸的是,我不认为真的有办法对某些第 3 方库的类型提示进行 "partial" 更改——至少,mypy 不行。
我会尝试以下三个选项之一:
只需# type: ignore
属性访问:
def foo(u: URL) -> Tuple[str, str, str]:
sr: SplitResult = u._val # type: ignore
return sr[:3]
此类型忽略将抑制在该行上生成的任何错误消息。如果您打算采用这种方法,我还建议使用带有 --warn-unused-ignores
标志的 运行 mypy,它将报告任何冗余和未使用的 # type: ignore
语句。这个特定的 # type: ignore
不太可能会变得多余,因为 mypy updates/as 是第三方库更新的存根,但它是一个很好的标志,可以在一般情况下启用。
与这个库的维护者谈谈,看看他们是否愿意为此属性添加类型提示(即使它是私有的),或者通过一些新的 API.
如果有帮助,是一些先例,可以在 Typeshed(标准库的类型存储库)中为私有或未记录的属性添加类型提示——请参阅 "What to include" section in their contribution guidelines.
如果库维护者不愿意添加这个属性,你总是可以只为这个库分叉存根,对分叉的存根进行更改,然后开始使用它。
我个人会先尝试解决方案 2,然后再尝试解决方案 1,但这就是我。
一个选项是根据您想要 'extend' 的 class 创建一个新的 class。当我想要自动完成我正在使用的数据时,我为 Pandas DataFrame
对象执行此操作。
import pandas as pd
class TitanicDataFrame(pd.DataFrame):
PassengerId: pd.Series
Survived: pd.Series
Name: pd.Series
Sex: pd.Series
Age: pd.Series
df: TitanicDataFrame = pd.read_csv('data/titanic.csv')
mean_age = df.Age.mean()
请注意 TitanicDataFrame
class 实际上并未使用(作为 class),它仅用作类型(因此在运行时被忽略)。
我正在使用 yarl
库的 URL
对象。
它有一个准私有属性,._val
,它是一个 urllib.parse.SplitResult
对象,但在 yarl/__init__.pyi
中没有类型注释。 (可以理解,如果开发者不想正式将其作为 public API 的一部分。)
但是,我选择使用 URL._val
风险自负。一个虚拟的例子:
# urltest.py
from urllib.parse import SplitResult
from typing import Tuple
from yarl import URL
def foo(u: URL) -> Tuple[str, str, str]:
sr: SplitResult = u._val
return sr[:3]
但是mypy
不喜欢这样,因为它抱怨:
$ mypy urltest.py
"URL" has no attribute "_val"
那么,在我自己的项目中,我如何才能 "tack on"(或扩展)实例属性注释到 URL
,以便它可以在我的项目的其余部分中使用?即
from yarl import URL
URL._val: SplitResult
# ...
(mypy也不喜欢这样;"Type cannot be declared in assignment to non-self attribute.")
更新
我尝试在 stubs/yarl/__init__.pyi
:
from urllib.parse import SplitResult
class URL:
_val: SplitResult
然后按照stub files中的描述设置export MYPYPATH='.../stubs'
。但是,此 覆盖,而不是扩展, 现有注释,因此 但 ._val
的所有内容都会抛出错误:
error: "URL" has no attribute "with_scheme"
error: "URL" has no attribute "host"
error: "URL" has no attribute "fragment"
...等等。
一种可能性是简单地忽略此赋值的 u
类型:
def foo(u: URL) -> Tuple[str, str, str, str]:
sr: SplitResult = typing.cast(typing.Any, u)._val
return sr[:3]
mypy
将假定您知道自己在做什么,并且 u
具有类型为 str
.
_val
属性
不幸的是,我不认为真的有办法对某些第 3 方库的类型提示进行 "partial" 更改——至少,mypy 不行。
我会尝试以下三个选项之一:
只需
# type: ignore
属性访问:def foo(u: URL) -> Tuple[str, str, str]: sr: SplitResult = u._val # type: ignore return sr[:3]
此类型忽略将抑制在该行上生成的任何错误消息。如果您打算采用这种方法,我还建议使用带有
--warn-unused-ignores
标志的 运行 mypy,它将报告任何冗余和未使用的# type: ignore
语句。这个特定的# type: ignore
不太可能会变得多余,因为 mypy updates/as 是第三方库更新的存根,但它是一个很好的标志,可以在一般情况下启用。与这个库的维护者谈谈,看看他们是否愿意为此属性添加类型提示(即使它是私有的),或者通过一些新的 API.
如果有帮助,是一些先例,可以在 Typeshed(标准库的类型存储库)中为私有或未记录的属性添加类型提示——请参阅 "What to include" section in their contribution guidelines.
如果库维护者不愿意添加这个属性,你总是可以只为这个库分叉存根,对分叉的存根进行更改,然后开始使用它。
我个人会先尝试解决方案 2,然后再尝试解决方案 1,但这就是我。
一个选项是根据您想要 'extend' 的 class 创建一个新的 class。当我想要自动完成我正在使用的数据时,我为 Pandas DataFrame
对象执行此操作。
import pandas as pd
class TitanicDataFrame(pd.DataFrame):
PassengerId: pd.Series
Survived: pd.Series
Name: pd.Series
Sex: pd.Series
Age: pd.Series
df: TitanicDataFrame = pd.read_csv('data/titanic.csv')
mean_age = df.Age.mean()
请注意 TitanicDataFrame
class 实际上并未使用(作为 class),它仅用作类型(因此在运行时被忽略)。