def f(x, y):
return x & 1 == 0 and y > 0
g = lambda x, y: x & 1 == 0 and y > 0
现在 Haskell 中也有同样的事情:
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 必须具有:
__matmul__
(@
)bool
我知道我可以粗略地通过以下方式做到这一点:(文档字符串和/或 PEP484)与
abc
;用于课程。但是对于模块中的“松散”功能我该怎么办?
通常您有三种可能的方法:
1 + 'foo'
是 TypeError
)。您的具体观点:
- 数量为 5
定义五个参数:
def f(a, b, c, d, e): ...
- 每个参数必须是数字
静态类型注释:
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
则不能。考虑到这一点的冗长和鸭子打字哲学,这些方法并不是很Pythonic。
- 每个参数必须实现
(__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, ...): ...
在实践中,您可能希望将其与之前的“每个参数必须是数字”和满足这两个要求的类型的类型提示结合起来。
- 返回
bool
def f(...) -> bool: ...
特别是考虑到
@
运算符主要由 numpy 等第三方软件包使用,实际上,此类函数的最 Pythonic 实现可能是这样的:
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]): ...
我发现的一个技巧是使用回调协议:
from typing import Optional, Iterable
from sys import version_info
if version_info > (3, 8):
from typing import List, Literal, Optional, Protocol, Tuple, Union
else:
from typing_extensions import Protocol
class Combiner(Protocol):
def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ...
适用于我的问题,只需复制:
def combiner0(*vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: return []
def combiner1(*vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: return []
# Unused var just for type checking
_combiner: Combiner = combiner0
_combiner: Combiner = combiner1
del _combiner