在Python中为模块(而不是类)中的函数实现类型?

2024-10-05 10:54:23 发布

您现在位置:Python中文网/ 问答频道 /正文

def f(x, y):
    return x & 1 == 0 and y > 0

g = lambda x, y: x & 1 == 0 and y > 0

现在哈斯凯尔也发生了同样的事情:

import Data.Bits

f :: Int -> Int -> Bool
f x y = (.&.) x 1 == 0 && y > 0

这是可行的,但这不可行:

g = \x y -> (.&.) x 1 == 0 && y > 0

这是一个错误:

someFunc :: IO ()
someFunc = putStrLn $ "f 5 7: " ++ ( show $ f 5 7 ) ++ "\tg 5 7: " ++ ( show $ g 5 7 )

• Ambiguous type variable ‘a0’ arising from the literal ‘1’
  prevents the constraint ‘(Num a0)’ from being solved.
  Relevant bindings include
    x :: a0 (bound at src/Lib.hs:13:6)
    g :: a0 -> Integer -> Bool (bound at src/Lib.hs:13:1)
  Probable fix: use a type annotation to specify what ‘a0’ should be.
  These potential instances exist:
    instance Num Integer -- Defined in ‘GHC.Num’
    instance Num Double -- Defined in ‘GHC.Float’
    instance Num Float -- Defined in ‘GHC.Float’
    ...plus two others
    ...plus one instance involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the second argument of ‘(.&.)’, namely ‘1’
  In the first argument of ‘(==)’, namely ‘(.&.) x 1’
  In the first argument of ‘(&&)’, namely ‘(.&.) x 1 == 0’
   |
13 | g = \x y -> (.&.) x 1 == 0 && y > 0
   |                     ^

如何在Python中获得相同的错误?-当输入与预期不匹配时,我如何获得错误

具体来说,我如何说函数/lambda必须具有:

  • 5的算术数
  • 每个参数必须是数字
  • 每个参数必须实现__matmul__@
  • 返回bool

我知道我可以用:(docstrings和/或PEP484)和abc大致做到这一点;上课。但对于模块中的“松散”函数,我能做些什么


Tags: andoftheinstancelambdain错误float
1条回答
网友
1楼 · 发布于 2024-10-05 10:54:23

通常有三种可能的方法:

  1. 让运行时为不兼容的操作生成错误(例如1 + 'foo'TypeError
  2. 对某些属性执行显式运行时检查;尽管这通常是不鼓励的,因为Python经常使用duck类型
  3. 使用类型注释和静态类型检查器

你的具体观点:

  • arity of 5

定义五个参数:

def f(a, b, c, d, e): ...
  • each argument must be numerical

静态类型批注之一:

def f(a: int, b: int, c: int, d: int, e: int): ...

和/或运行时检查:

def f(a, b, c, d, e):
    assert all(isinstance(i, int) for i in (a, b, c, d, e))

def f(a, b, c, d, e):
    if not all(isinstance(i, int) for i in (a, b, c, d, e)):
        raise TypeError

assert用于调试目的,可以禁用,而显式if..raise则不能。考虑到这种方法的冗长性和duck类型哲学,这些方法并不是很像python

  • each argument must implement __matmul__ (@)

如果传递的值不支持该操作,最实用的方法可能是让运行时在本质上引发错误,即只执行以下操作:

def f(a, b):
    return a @ b  # TypeError: unsupported operand type(s) for @: ... and ...

如果要对此进行静态类型检查,可以使用typing.Protocol

from typing import Protocol

class MatMullable(Protocol):
    def __matmul__(self, other) -> int:
        pass

def f(a: MatMullable, ...): ...

在实践中,您可能希望将其与前面的“每个参数都必须是数字”和类型提示结合起来,以获得满足这两个要求的类型

  • return bool
def f(...) -> bool: ...

特别是考虑到@运算符主要由第三方软件包(如numpy)使用,在实践中,此类函数最具python风格的实现可能是以下几点:

import numpy as np
from numpy import ndarray

def f(a: ndarray, b: ndarray, c: ndarray, d: ndarray, e: ndarray) -> bool:
    return np.linalg.det(a @ b @ c @ d @ e) > 0  # or whatever

您不能直接将相同的类型期望从Haskell转换为Python。Haskell是一种疯狂的强类型语言,而Python则几乎完全相反


要键入接受此类函数作为参数的高阶函数,请使用typing.Callable

from typing import Callable

def hof(f: Callable[[ndarray, ndarray, ndarray, ndarray, ndarray], bool]): ...

相关问题 更多 >

    热门问题