如何指定pandas Series的变量类型(string或TypeVar)?

vwkv1x7d  于 2023-04-19  发布在  其他
关注(0)|答案(2)|浏览(111)

我想使用类型提示来实现以下内容:

def fo() -> pd.Series[np.float64]:
   return pd.Series(np.float64[0])

这样不行
从这个答案:How to specify the type of pandas series elements in type hints?
我知道我可以使用:

def fo() -> "pd.Series[np.float64]":
   return pd.Series(np.float64[0])

或者:

from typing import (
    TypeVar
)

SeriesFloat64 = TypeVar('pd.Series[np.float64]')
def fo() -> SeriesFloat64:
   return pd.Series(np.float64[0])

为什么我更喜欢其中一个?

6uxekuva

6uxekuva1#

您引用的两个“解决方案”都是错误的

我从第二个开始:

from typing import TypeVar
import numpy as np, pandas as pd

SeriesFloat64 = TypeVar('pd.Series[np.float64]')

def fo() -> SeriesFloat64:
    return pd.Series(np.float64(0))

这个类型变量 * 技术上 * 是有效的注解吗?是的。它指定了泛型pd.Series吗?不是。
首先,正如@jonrsharpe所指出的,这打破了用name参数初始化类型变量的惯例,该参数对应于变量的实际名称。更重要的是,既没有指定bound也没有指定constraints,这意味着你还不如这样写:

from typing import TypeVar
import numpy as np, pandas as pd

T = TypeVar("T")  # which is the same as `TypeVar("T", bound=typing.Any)`

def fo() -> T:
    return pd.Series(np.float64(0))

这至少解决了名称问题,但它不会指定任何关于fo()的返回类型。实际上,mypy将正确指出以下内容:

error: Incompatible return value type (got "Series[Any]", expected "T")  [return-value]

这已经给出了关于pd.Series规范我们能做什么和不能做什么的提示,这将我们引向第二个“解决方案”:

import numpy as np, pandas as pd

def fo() -> "pd.Series[np.float64]":
    return pd.Series(np.float64(0))

这是等价的,顺便说一下:(不需要引号)

from __future__ import annotations
import numpy as np, pandas as pd

def fo() -> pd.Series[np.float64]:
    return pd.Series(np.float64(0))

这是错误的,因为泛型Series的类型参数不接受np.float64mypy再次指出了这一点:

error: Value of type variable "S1" of "Series" cannot be "floating"  [type-var]

如果我们查看core.series.Seriespandas-stubs源代码(截至目前),我们会看到Series继承自typing.Generic[S1]。当我们转到_typing.S1的定义时,我们可以看到该类型变量的约束。numpy float不在其中,但我们确实找到了内置的float。这意味着什么?
我们知道np.float64确实继承自常规的float但它也继承自np.floating,这就是问题所在。
类型约束导致推断的类型 * 恰好 * 是约束类型[.]之一
这意味着你 * 不 * 允许使用np.float64代替前面提到的S1来指定Series类型。

更好的方式

在我看来,输入hint函数的“最正确”方法是这样做:

from __future__ import annotations
import numpy as np, pandas as pd

def fo() -> pd.Series[float]:
    return pd.Series(np.float64(0))

这正确地使用了Series泛型,提供了一个类型变量,该变量既符合定义的类型约束**,又表示函数返回的系列的元素类型,该类型尽可能接近实际类型,因为np.float64 * 确实 * 继承自float
它还通过了严格的mypy检查。

添加有用信息

需要注意的是,这样会丢失series * 实际上 * 包含64位浮点数的信息。如果你想让函数的签名/文档反映这种细微差别,你可以简单地设置一个自定义类型别名:

from __future__ import annotations
import numpy as np, pandas as pd

float64 = float

def fo() -> pd.Series[float64]:
    return pd.Series(np.float64(0))

现在调用help(fo)得到:

...
fo() -> 'pd.Series[float64]'

但重要的是要注意,这只是为了 * 你的 * 利益,对静态类型检查器绝对没有任何作用。

pd.Series类型限制

另一件值得一提的事情是,到目前为止,pd.Series的许多返回单个元素的方法上都没有有用的注解,例如通过方括号[]访问的__getitem__方法。假设我这样做:

...
series = fo()
x = series[0]
print(x, type(x))
y = int(x)

输出是0.0 <class 'numpy.float64'>,但是类型检查器不知道xnp.float64还是任何float。(事实上,我的PyCharm在y = int(x)上抱怨,因为它认为x是一个时间戳,不管出于什么原因。)
这只是为了说明,到目前为止,在处理pd.Series时,您可能不会得到任何有用的自动建议,即使您或多或少正确地注解了类型。
希望这能帮上忙。

lymgl2op

lymgl2op2#

我不能说float 64是否会在pandas Series中工作-从2.0开始,Arrow现在在下面而不是numpy。然而,简单的做法是使用typing.TypeVar

from typing import TypeVar

float64 = float

def fo() -> TypeVar("pd.Series(float)"):
    return pd.Series(np.float64(0))

fo()

相关问题