Rust风格的类型安全结果类型
项目描述
safetywrap
适用于Python值的完全类型安全的Rust风格包装类型
摘要
此库提供了两种主要的包装:Result
和Option
。这些类型允许您指定类型安全的代码,有效地处理错误或缺失的数据,而无需使用深度嵌套的if语句和大量的try-except块。
这是通过允许您在某种量子叠加中对Option
或Result
进行操作来实现的,其中Option
可以是Some
或Nothing
,而Result
可以是Ok
或Err
。在任一情况下,类型上的所有方法都工作相同,使您能够优雅地处理两种情况。
Result[T, E]
可以是Ok[T]
或Err[E]
的实例,而Option[T]
可以是Some[T]
或Nothing
的实例。无论如何,您都可以将它们视为相同,直到您真正需要获取包装的值。
因此,而不是这样
for something in "value", None:
if something is not None:
val = something.upper()
else:
val = "DEFAULT"
print(val)
您可以这样操作
for something in Some("value"), Nothing():
print(something.map(str.upper).unwrap_or("DEFAULT"))
而不是这样
for jsondata in '{"value": "myvalue"}', '{badjson':
try:
config = capitalize_keys(json.loads(jsondata))
except Exception:
config = get_default_config()
print(config["value"])
您可以这样操作
for jsondata in '{"value": "myvalue"}', '{badjson':
print(
Result.of(json.loads, jsondata)
.map(capitalize_keys)
.unwrap_or_else(get_default_config)["value"]
)
这两个示例展示了如何使用这些类型安全的包装器使编写和理解更加容易。有关更多内容,请参阅示例部分,以及用法部分以获取提供的完整功能集。
这些类型在很大程度上受到Rust中的Result和Option类型的影响。
为mypy或您喜欢的Python类型检查器提供了详尽的类型规范,因此您可以装饰函数的输入和输出,使其返回Result
和Option
类型,并在提供参数或传递返回值时获得有用的反馈。
赞助
该项目由我的雇主Bestow, Inc.开发和慷慨赞助。在Bestow,我们致力于通过提供简单、便捷的保险,在线五分钟内即可购买,无需医生就诊和繁琐的手续,来实现人寿保险的民主化。
我们几乎一直在招聘优秀的开发者,如果您想加入我们,请查看我们的职业页面!
目录
- safetywrap
- 摘要
- 目录
- 示例
- 用法
- Result[T, E]
- Option[T]
- Option构造函数
- Option方法
- Option.and_
- Option.or_
- Option.xor
- Option.and_then
- Option.flatmap
- Option.or_else
- Option.expect
- Option.raise_if_nothing
- Option.filter
- Option.is_nothing
- Option.is_some
- Option.iter
- Option.map
- Option.map_or
- Option.map_or_else
- Option.ok_or
- Option.ok_or_else
- Option.unwrap
- Option.unwrap_or
- Option.unwrap_or_else
- Option魔术方法
- 性能
- 贡献
示例
通常,这些示例从简单到复杂。有关完整的API规范,请参阅下方的用法。
通过值获取枚举成员,返回成员或None
import typing as t
from enum import Enum
from result_types import Option, Result, Some
T = t.TypeVar("T", bound=Enum)
def enum_member_for_val(enum: t.Type[T], value: t.Any) -> t.Optional[t.Any]:
"""Return Some(enum_member) or Nothing()."""
# Enums throw a `ValueError` if the value isn't present, so
# we'll either have `Ok(enum_member)` or `Err(ValueError)`.
# We unwrap and return the member if it's Ok, otherwise, we just
# return None
return Result.of(enum, value).unwrap_or(None)
通过值获取枚举成员,返回Option
import typing as t
from enum import Enum
from result_types import Option, Result, Some
T = t.TypeVar("T", bound=Enum)
def enum_member_for_val(enum: t.Type[T], value: t.Any) -> Option[T]:
"""Return Some(enum_member) or Nothing()."""
# Enums throw a `ValueError` if the value isn't present, so
# we'll either have `Ok(enum_member)` or `Err(ValueError)`.
# Calling `ok()` on a `Result` returns an `Option`
return Result.of(enum, value).ok()
使用默认值序列化可能缺少键的字典
import json
from result_types import Result
def serialize(data: t.Dict[str, t.Union[int, str, float]]) -> str:
"""Serialize the data.
Absent keys are "[absent]", rather than null. This allows us to maintain
information about whether a key was present or actually set to None.
"""
keys = ("first", "second", "third", "fourth")
# We can even use Result to catch any JSON serialization errors, so that
# this function will _always_ return a string!
Result.of(
json.dumps,
# Result.of() will intercept the KeyError and return an Err. We use
# `unwrap_or()` to discard the error and return the "[absent]" string
# instead; if the key was present, the Result was Ok, and we just
# return that value.
{k: Result.of(lambda: data[k]).unwrap_or("[absent]") for k in keys}
).unwrap_or("Could not serialize JSON from data!")
发起HTTP请求,如果状态码是200,则将正文转换为JSON并返回data
键。如果出现错误或data
键不存在,则返回错误字符串
from functools import partial
import requests
from requests import Response
from result_types import Option, Result
def get_data(url: str) -> str:
"""Get the data!"""
# We need to do manual type assignment sometimes when the code
# we're wrapping does not provide types.
# If the wrapped function raises any Exception, `res` will be
# Err(Exception). Otherwise it will be `Ok(Response)`.
res: Result[Response, Exception] = Result.of(requests.get, url)
return (
# We start as a `Result[Response, Exception]`
res
# And if we were an Err, map to a `Result[Response, str]`
.map_err(str)
# If we were Ok, and_then (aka flatmap) to a new `Result[Response, str]`
.and_then(lambda res: (
# Our return value starts as a `Result[Response, Response]`
Result.ok_if(lambda r: r.status_code == 200, res).map_err(
# So we map it to a `Result[Response, str]`
lambda r: str(f"Bad status code: {r.status_code}")
)
))
# We are now a `Result[Response, str]`, where we are only Ok if
# our status code was 200.
# Now we transition to a `Result[dict, str]`
.and_then(lambda res: Result.of(res.json).map_err(str))
# And to a `Result[Option[str], str]`
.map(lambda js: Option.of(js.get("data")).map(str))
# And to a `Result[str, str]`
.and_then(lambda data: data.ok_or("No data key in JSON!"))
# If we are an error, convert us to an Ok with the error string
.or_else(Ok)
# And now we get either the Ok string or the Err string!
.unwrap()
)
用法
Result[T, E]
Result表示可能处于Ok
状态或Err
状态的一些值。
Result构造函数
Ok
Ok(value: T) -> Result[T, E]
直接使用值构造一个Ok
Result。
示例
def check_value_not_negative(val: int) -> Result[int, str]:
"""Check that a value is not negative, or return an Err."""
if val >= 0:
return Ok(val)
return Err(f"{val} is negative!")
Err
Err(value: E) -> Result[T, E]
直接使用值构造一个Err
Result。
示例
def check_value_not_negative(val: int) -> Result[int, str]:
"""Check that a value is not negative, or return an Err."""
if val >= 0:
return Ok(val)
return Err(f"{val} is negative!")
Result.of
Result.of(fn: Callable[..., T], *args: t.Any, catch: t.Type[E], **kwargs) -> Result[T, E]
使用提供的参数调用函数。如果没有抛出错误,则返回Ok(result)
。否则,返回Err(exception)
。默认情况下,会捕获Exception
,但也可以通过catch
关键字参数提供不同的错误类型。
E
的类型必须是Exception
或其子类之一。
此构造函数旨在在包装其他API、内置函数等时非常有用。
请注意,由于mypy中的一个bug(参见https://github.com/python/mypy/issues/3737),有时您需要显式指定catch
关键字参数,即使将其设置为默认值(Exception
)。这种情况并不总是发生,但发生时,您会看到mypy认为Result
的类型是Result[SomeType, <nothing>]
。
示例
import json
def parse_json(string: str) -> Result[dict, Exception]:
"""Parse a JSON object into a dict."""
return Result.of(json.loads, string)
Result.collect
Result.collect(iterable: Iterable[T, E]) -> Result[Tuple[T, ...], E]
将一系列结果转换为单个结果。如果所有结果都是Ok,则Ok值将被收集到最终结果的元组中。如果任何结果是Err,则直接返回Err结果。
示例
assert Result.collect([Ok(1), Ok(2), Ok(3)]) == Ok((1, 2, 3))
assert Result.collect([Ok(1), Err("no"), Ok(3)]) == Err("no")
Result.err_if
Result.err_if(predicate: t.Callable[[T], bool], value: T) -> Result[T, T]
对某个值运行一个谓词,如果谓词返回True,则返回Err(val)
,如果谓词返回False,则返回Ok(val)
。
示例
from requests import Response
def checked_response(response: Response) -> Result[Response, Response]:
"""Try to get a response from the server."""
return Result.err_if(lambda r: r.status_code >= 300, response)
Result.ok_if
Result.ok_if(predicate: t.Callable[[T], bool], value: T) -> Result[T, T]
对某个值运行一个谓词,如果谓词返回True,则返回Ok(val)
,如果谓词返回False,则返回Err(val)
。
示例
def checked_data(data: dict) -> Result[dict, dict]:
"""Check if data has expected keys."""
expected_keys = ("one", "two", "three")
return Result.ok_if(lambda d: all(k in d for k in expected_keys), data)
Result方法
Result.and_
Result.and_(self, res: Result[U, E]) -> Result[U, E]
如果此Result是Ok
,则返回res
。如果此结果为Err
,则返回此Result。这可以在遇到第一个错误时截断Result链。
示例
assert Ok(5).and_(Ok(6)) == Ok(6)
assert Err(1).and_(Ok(6)) == Err(1)
assert Err(1).and_(Err(2)).and_(Ok(5)) == Err(1)
assert Ok(5).and_(Err(1)).and_(Ok(6)) == Err(1)
Result.or_
Result.or_(self, res: Result[T, F]) -> Result[T, F]
如果此Result是Err
,则返回res
。否则,返回此Result。
示例
assert Err(1).or_(Ok(5)) == Ok(5)
assert Err(1).or_(Err(2)) == Err(2)
assert Ok(5).or_(Ok(6)) == Ok(5)
assert Ok(5).or_(Err(1)) == Ok(5)
Result.and_then
Result.and_then(self, fn: t.Callable[[T], Result[U, E]]) -> Result[U, E]
如果此Result是Ok
,则使用此Result的包装值调用提供的函数,并返回该函数的结果。这允许轻松地将多个生成Result的调用链接在一起以生成最终Result。此方法是对Result.flatmap
的别名
示例
assert Ok(5).and_then(lambda val: Ok(val + 1)) == Ok(6)
assert Err(1).and_then(lambda val: Ok(val + 1)) == Err(1)
Result.flatmap
Result.flatmap(self, fn: t.Callable[[T], Result[U, E]]) -> Result[U, E]
如果此Result是Ok
,则使用此Result的包装值调用提供的函数,并返回该函数的结果。这允许轻松地将多个生成Result的调用链接在一起以生成最终Result。此方法是对Result.and_then
的别名
示例
assert Ok(5).flatmap(lambda val: Ok(val + 1)) == Ok(6)
assert Err(1).flatmap(lambda val: Ok(val + 1)) == Err(1)
Result.or_else
Result.or_else(self, fn: t.Callable[[E], Result[T, F]]) -> Result[T, F])
如果此结果为Err
,则使用此Result的包装错误值调用提供的函数,并返回该函数的结果。这允许以仍然返回最终Result的方式轻松处理潜在的错误。
示例
assert Ok(5).or_else(Ok) == Ok(5)
assert Err(1).or_else(Ok) == Ok(1)
Result.err
Result.err(self) -> Option[E]
将此Result转换为Option,如果此Result是Err
,则返回Some(err_val),如果此Result是Ok
,则返回Nothing()。
示例
assert Ok(5).err() == Nothing()
assert Err(1).err() == Some(1)
Result.ok
Result.ok(self) -> Option[T]
将此Result转换为Option,如果此Result是Ok
,则返回Some(val),如果此结果为Err
,则返回Nothing()。
示例
assert Ok(5).ok() == Some(5)
assert Err(1).ok() == Nothing()
Result.expect
Result.expect(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -> T
如果此Result是Ok
,则返回包装的值。否则,引发一个错误,使用提供的消息和字符串化的错误值实例化。默认情况下,引发一个RuntimeError
,但可以使用exc_cls
关键字参数提供一个替代错误。此方法是对Result.raise_if_err
的别名。
示例
import pytest
with pytest.raises(RuntimeError) as exc:
Err(5).expect("Bad value")
assert str(exc.value) == "Bad value: 5"
assert Ok(1).expect("Bad value") == 1
Result.raise_if_err
Result.raise_if_err(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -> T
如果此Result是Ok
,则返回包装的值。否则,引发一个错误,使用提供的消息和字符串化的错误值实例化。默认情况下,引发一个RuntimeError
,但可以使用exc_cls
关键字参数提供一个替代错误。此方法是对Result.expect
的别名。
示例
import pytest
with pytest.raises(RuntimeError) as exc:
Err(5).raise_if_err("Bad value")
assert str(exc.value) == "Bad value: 5"
assert Ok(1).raise_if_err("Bad value") == 1
Result.expect_err
Result.expect_err(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -> E
如果这个Result是Err
,则返回包装的值。否则,抛出一个错误,错误实例化提供的信息和Ok值的字符串表示。默认情况下,抛出RuntimeError
,但可以使用exc_cls
关键字参数提供替代错误。
示例
import pytest
with pytest.raises(RuntimeError) as exc:
Ok(5).expect_err("Unexpected good value")
assert str(exc.value) == "Unexpected good value: 5"
assert Err(1).expect_err("Unexpected good value") == 1
Result.is_err
Result.is_err(self) -> bool
如果这个Result是Err
,则返回True,如果这个Result是Ok
,则返回False。
示例
assert Err(1).is_err() is True
assert Ok(1).is_err() is False
Result.is_ok
Result.is_ok(self) -> bool
如果这个Result是Ok
,则返回True,如果这个Result是Err
,则返回False。
示例
assert Ok(1).is_err() is True
assert Err(1).is_err() is False
Result.iter
Result.iter(self) -> Iterator[T]
如果这个Result是Ok
,则返回一个长度为1的迭代器,遍历包装的值。否则,返回一个0长度的迭代器。
示例
assert tuple(Ok(1).iter()) == (1,)
assert tuple(Err(1).iter()) == ()
Result.map
Result.map(self, fn: t.Callable[[T], U]) -> Result[U, E]
如果这个Result是Ok
,则将提供的函数应用于包装的值,并返回一个包含函数结果的新Ok
Result。如果这个Result是Err
,则不应用函数并返回此Result不变。
警告:在应用提供的函数时不会进行错误检查,并且不会捕获应用函数时抛出的异常。如果您需要带有错误处理的映射,请考虑使用与Result.of
构造函数一起使用的and_then
(也称为flatmap
),例如:assert Ok(0).and_then(partial(Result.of, lambda i: 10 / i)) == Err(ZeroDivisionError('division by zero'))
示例
assert Ok(1).map(str) == Ok("1")
assert Err(1).map(str) == Err(1)
Result.map_err
Result.map_err(self, fn: t.Callable[[E], F]) -> Result[T, F]
如果这个Result是Err
,则将提供的函数应用于包装的值,并返回一个包含函数结果的新Err
Result。如果这个Result是Ok
,则不应用函数并返回此Result不变。
警告:在应用提供的函数时不会进行错误检查,并且不会捕获应用函数时抛出的异常。
示例
assert Err(1).map_err(lambda i: i + 1) == Err(2)
assert Ok(1).map_err(lambda i: i + 1) == Ok(1)
Result.unwrap
Result.unwrap(self) -> T
如果这个Result是Ok
,则返回包装的值。如果这个Result是Err
,则抛出RuntimeError
。
示例
import pytest
assert Ok(1).unwrap() == 1
with pytest.raises(RuntimeError):
Err(1).unwrap()
Result.unwrap_err
Result.unwrap_err(self) -> E
如果这个Result是Err
,则返回包装的值。如果这个Result是Ok
,则抛出RuntimeError
。
示例
import pytest
assert Err(1).unwrap() == 1
with pytest.raises(RuntimeError):
Ok(1).unwrap()
Result.unwrap_or
Result.unwrap_or(self, alternative: U) -> t.Union[T, U]
如果这个Result是Ok
,则返回包装的值。如果这个Result是Err
,则返回提供的替代值。
示例
assert Ok(1).unwrap_or(5) == 1
assert Err(1).unwrap_or(5) == 5
Result.unwrap_or_else
Result.unwrap_or_else(self, fn: t.Callable[[E], U]) -> t.Union[T, U]
如果这个Result是Ok
,则返回包装的值。如果这个Result是Err
,则调用提供的函数,该函数以包装的错误值作为参数,并返回结果。
示例
assert Ok(1).unwrap_or_else(str) == 1
assert Err(1).unwrap_or_else(str) == "1"
Result魔术方法
Result.iter
Result.__iter__(self) -> t.Iterator[T]
实现迭代协议,允许遍历Result.iter
的结果。如果这个Result是Ok
,则返回一个包含包装值的长度为1的迭代器。如果这个Result是Err
,则返回一个0长度的迭代器。
示例
# Can be passed to methods that take iterators
assert tuple(Ok(1)) == (1,)
assert tuple(Err(1)) == ()
# Can be used in `for in` constructs, including comprehensions
assert [val for val in Ok(5)] == [5]
assert [val for val in Err(5)] == []
# More for-in usage.
for val in Ok(5):
pass
assert val == 5
val = None
for val in Err(1):
pass
assert val is None
Result.eq
Result.__eq__(self, other: Any) -> bool
启用使用==
的相等性检查。
将Result与other
进行比较。如果other
是具有相同包装值的相同类型的Result,则返回True。否则,返回False。
示例
assert (Ok(5) == Ok(5)) is True
assert (Ok(5) == Ok(6)) is False
assert (Ok(5) == Err(5)) is False
assert (Ok(5) == 5) is False
Result.ne
Result.__ne__(self, other: Any) -> bool
启用使用!=
的不等性检查。
将Result与other
进行比较。如果other
是具有相同包装值的相同类型的Result,则返回False。否则,返回True。
示例
assert (Ok(5) != Ok(5)) is False
assert (Ok(5) != Ok(6)) is True
assert (Ok(5) != Err(5)) is True
assert (Ok(5) != 5) is True
Result.str
Result.__str__(self) -> str
启用使用str()
的有用的字符串表示。
示例
assert str(Ok(5)) == "Ok(5)"
assert str(Err(5)) == "Err(5)"
Result.repr
Result.__repr__(self) -> str
启用使用repr()
的有用的字符串表示。
示例
assert repr(Ok(5)) == "Ok(5)"
assert repr(Err(5)) == "Err(5)"
Option[T]
Option表示Some
值或Nothing
。
Option构造函数
Some
Some(value: T) -> Option[T]
直接使用值构造一个Some
Option。
示例
def file_contents(path: str) -> Option[str]:
"""Return the file contents or Nothing."""
try:
with open(path) as f:
return Some(f.read())
except IOError:
return Nothing()
Nothing
Nothing() -> Option[T]
直接使用值构造一个Nothing
Option。
注意:作为实现细节,Nothing
被实现为一个单例,以避免在第一次之后创建任何 Nothing
时的实例化时间。然而,由于这是一个实现细节,Nothing
选项仍应使用 ==
而不是 is
进行比较。
示例
def file_contents(path: str) -> Option[str]:
"""Return the file contents or Nothing."""
try:
with open(path) as f:
return Some(f.read())
except IOError:
return Nothing()
Option.of
Option.of(value: t.Optional[T]) -> Option[T]
将可选值转换为 Option。如果值不是 None
,则返回 Some(value)
。否则,如果值是 None
,则返回 Nothing()
。
示例
assert Option.of(None) == Nothing()
assert Option.of({}.get("a")) == Nothing()
assert Option.of("a") == Some("a")
assert Option.of({"a": "b"}) == Some("b")
Option.nothing_if
Option.nothing_if(predicate: t.Callable[[T], bool], value: T) -> Option[T]
使用提供的值调用提供的谓词函数。如果谓词返回 True,则返回 Nothing()
。如果谓词返回 False,则返回 Some(value)
。
示例
assert Option.nothing_if(lambda val: val.startswith("_"), "_private") == Nothing()
assert Option.nothing_if(lambda val: val.startswith("_"), "public") == Some("public")
Option.some_if
Option.some_if(predicate: t.Callable[[T], bool], value: T) -> Option[T]
使用提供的值调用提供的谓词函数。如果谓词返回 True,则返回 Some(value)
。如果谓词返回 False,则返回 Nothing()
。
示例
assert Option.some_if(bool, [1, 2, 3]) == Some([1, 2, 3])
assert Option.some_if(bool, []) == Nothing()
Option.collect
Option.collect(options: t.Iterable[Option[T]]) -> Option[t.Tuple[T, ...]]
将一系列 Option 收集到单个 Option 中。
如果所有选项都是 Some[T]
,则结果是 Some[Tuple[T, ...j]]
。如果有任何选项是 Nothing
,则结果是 Nothing
。
示例
assert Option.collect([Some(1), Some(2), Some(3)]) == Some((1, 2, 3))
assert Option.collect([Some(1), Nothing(), Some(3)]) == Nothing()
Option方法
Option.and_
Option.and_(alternative: Option[U]) -> Option[U]
如果此 Option 是 Nothing
,则返回其未更改。否则,如果此 Option 是 Some
,则返回提供的 alternative
Option。
示例
assert Some(1).and_(Some(2)) == Some(2)
assert Nothing().and_(Some(2)) == Nothing()
assert Some(1).and_(Nothing()) == Nothing()
assert Nothing().and_(Nothing()) == Nothing()
assert Some(1).and_(Nothing()).and_(Some(2)) == Nothing()
Option.or_
Option.or_(alternative: Option[T]) -> Option[T]
如果此 Option 是 Nothing
,则返回提供的 alternative
Option。否则,如果此 Option 是 Some
,则返回其未更改。
示例
assert Some(1).or_(Some(2)) == Some(1)
assert Some(1).or_(Nothing()) == Some(1)
assert Nothing().or_(Some(1)) == Some(1)
assert Nothing().or_(Nothing()) == Nothing()
Option.xor
Option.xor(alternative: Option[T]) -> Option[T]
排他或。如果此 Option 和提供的 alternative
中的一个是 Some
,则返回 Some
Option。否则,返回 Nothing
。
示例
assert Some(1).xor(Nothing()) == Some(1)
assert Nothing().xor(Some(1)) == Some(1)
assert Some(1).xor(Some(2)) == Nothing()
assert Nothing().xor(Nothing()) == Nothing()
Option.and_then
Option.and_then(self, fn: t.Callable[[T], Option[U]]) -> Option[U]
如果此 Option 是 Some
,则使用包含的值调用提供的、返回 Option 的函数,并返回它返回的任何 Option。如果此 Option 是 Nothing
,则返回其未更改。此方法是对 Option.flatmap
的别名。
示例
assert Some(1).and_then(lambda i: Some(i + 1)) == Some(2)
assert Nothing().and_then(lambda i: Some(i + 1)) == Nothing()
Option.flatmap
Option.flatmap(self, fn: t.Callable[[T], Option[U]]) -> Option[U]
如果此 Option 是 Some
,则使用包含的值调用提供的、返回 Option 的函数,并返回它返回的任何 Option。如果此 Option 是 Nothing
,则返回其未更改。此方法是对 Option.and_then
的别名。
示例
assert Some(1).flatmap(Some) == Some(1)
assert Nothing().flatmap(Some) == Nothing()
Option.or_else
Option.or_else(self, fn: t.Callable[[], Option[T]]) -> Option[T]
如果此 Option 是 Nothing
,则调用提供的、返回 Option 的函数,并返回它返回的任何 Option。如果此 Option 是 Some
,则返回其未更改。
示例
assert Nothing().or_else(lambda: Some(1)) == Some(1)
assert Some(1).or_else(lambda: Some(2)) == Some(1)
Option.expect
Option.expect(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -> T
如果此 Option 是 Some
,则返回包装的值。否则,如果此 Option 是 Nothing
,则引发一个具有提供消息的错误。默认情况下,会引发一个 RuntimeError
,但可以通过 exc_cls
关键字参数提供自定义异常类。此方法是对 Option.raise_if_nothing
的别名。
示例
import pytest
with pytest.raises(RuntimeError) as exc:
Nothing().expect("Nothing here")
assert str(exc.value) == "Nothing here"
assert Some(1).expect("Nothing here") == 1
Option.raise_if_nothing
Option.raise_if_nothing(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -> T
如果此 Option 是 Some
,则返回包装的值。否则,如果此 Option 是 Nothing
,则引发一个具有提供消息的错误。默认情况下,会引发一个 RuntimeError
,但可以通过 exc_cls
关键字参数提供自定义异常类。此方法是对 Option.expect
的别名。
示例
import pytest
with pytest.raises(RuntimeError) as exc:
Nothing().raise_if_nothing("Nothing here")
assert str(exc.value) == "Nothing here"
assert Some(1).raise_if_nothing("Nothing here") == 1
Option.filter
Option.filter(self, predicate: t.Callable[[T], bool]) -> Option[T]
如果此选项为 Some
,则使用提供的谓词函数调用包装的值。如果谓词返回 True,则返回包含此 Option 的包装值的 Some
。如果谓词返回 False,则返回 Nothing
。如果此 Option 为 Nothing
,则返回它不变。
示例
def is_even(val: int) -> bool:
"""Return whether the value is even."""
return val % 2 == 0
assert Some(2).filter(is_even) == Some(2)
assert Some(1).filter(is_even) == Nothing()
assert Nothing().filter(is_even) == Nothing()
Option.is_nothing
Option.is_nothing(self) -> bool
如果此 Option 为 Nothing
,则返回 True。否则,如果此 Option 为 Some
,则返回 False。
示例
assert Nothing().is_nothing() is True
assert Some(1).is_nothing() is False
Option.is_some
Option.is_some(self) -> bool
如果此 Option 为 Some
,否则,如果此 Option 为 Nothing
,则返回 False。
示例
assert Some(1).is_some() is True
assert Nothing().is_some() is False
Option.iter
Option.iter(self) -> t.Iterator[T]
如果此 Option 为 Some
,则返回一个长度为一的迭代器,用于包装的值。否则,如果此 Option 为 Nothing
,则返回一个 0 长度的迭代器。
示例
assert tuple(Some(1).iter()) == (1,)
assert tuple(Nothing().iter()) == ()
Option.map
Option.map(self, fn: t.Callable[[T], U]) -> Option[U]
如果此 Option 为 Some
,则将提供的函数应用于包装的值,并返回包装函数结果的 Some
。如果此 Option 为 Nothing
,则返回不变的此 Option。
示例
assert Some(1).map(str) == Some("1")
assert Nothing().map(str) == Nothing()
assert Some(1).map(str).map(lambda x: x + "a").map(str.upper) == Some("1A")
Option.map_or
Option.map_or(self, default: U, fn: t.Callable[[T], U]) -> U
如果此 Option 为 Some
,则将提供的函数应用于包装的值并返回结果。如果此 Option 为 Nothing
,则返回提供的默认值。
示例
assert Some(1).map_or("no value", str) == "1"
assert Nothing().map_or("no value", str) == "no value"
Option.map_or_else
Option.map_or_else(self, default: t.Callable[[], U], fn: t.Callable[[T], U]) -> U
如果此 Option 为 Some
,则将提供的函数应用于包装的值并返回结果。如果此 Option 为 Nothing
,则调用提供的默认函数(不带参数)并返回结果。
示例
from datetime import datetime, date
assert Some("2005-08-28").map_or_else(
date.today,
lambda t: datetime.strptime(t, "%Y-%m-%d").date()
) == datetime(2005, 8, 28).date()
assert Nothing().map_or_else(
date.today,
lambda t: datetime.strptime(t, "%Y-%m-%d").date()
) == date.today()
Option.ok_or
Option.ok_or(self, err: E) -> Result[T, E]
如果此 Option 为 Some
,则返回包含值的 Ok
结果。否则,返回包含提供的错误的 Err
结果。
示例
assert Some(1).ok_or("no value!") == Ok(1)
assert Nothing().ok_or("no value!") == Err("no value!")
Option.ok_or_else
Option.ok_or_else(self, err_fn: t.Callable[[], E]) -> Result[T, E]
如果此 Option 为 Some
,则返回包含值的 Ok
结果。否则,调用提供的 err_fn
并将其返回值包装在 Err
结果中。
示例
from functools import partial
def make_err_msg(msg: str) -> str:
"""Make an error message with some starting text."""
return f"[MY_APP_ERROR] -- {msg}"
assert Some(1).ok_or_else(partial(make_err_msg, "no value!")) == Ok(1)
assert Nothing().ok_or_else(partial(make_err_msg, "no value!")) == Err(
"[MY_APP_ERROR] -- no value!"
)
Option.unwrap
Option.unwrap(self) -> T
如果此 Option 为 Some
,则返回包装的值。否则,引发一个 RuntimeError
。
示例
import pytest
assert Some(1).unwrap() == 1
with pytest.raises(RuntimeError):
Nothing().unwrap()
Option.unwrap_or
Option.unwrap_or(self, default: U) -> t.Union[T, U]
如果此 Option 为 Some
,则返回包装的值。否则,返回提供的默认值。
示例
assert Some(1),unwrap_or(-1) == 1
assert Nothing().unwrap_or(-1) == -1
Option.unwrap_or_else
Option.unwrap_or_else(self, fn: t.Callable[[], U]) -> t.Union[T, U]
如果此 Option 为 Some
,则返回包装的值。否则,返回提供的函数的结果。
示例
from datetime import date
assert Some(date(2001, 1, 1)).unwrap_or_else(date.today) == date(2001, 1, 1)
assert Nothing().unwrap_or_else(date.today) == date.today()
Option魔术方法
Option.iter
Option.__iter__(self) -> t.Iterator[T]
实现迭代协议,允许迭代 Option.iter
的结果。如果此 Option 为 Ok
,则返回一个包含包装值的长度为 1 的迭代器。否则,如果此 Option 为 Nothing
,则返回一个 0 长度的迭代器。
示例
# Can be passed to methods that take iterators
assert tuple(Some(1)) == (1,)
assert tuple(Nothing()j) == ()
# Can be used in `for in` constructs, including comprehensions
assert [val for val in Some(1)] == [1]
assert [val for val in Nothing()] == []
# More for-in usage.
for val in Some(1):
pass
assert val == 1
val = None
for val in Nothing():
pass
assert val is None
Option.eq
Option.__eq__(self, other: Any) -> bool
启用使用==
的相等性检查。
将此 Option 与 other
进行比较。如果 other
是与具有相同包装值的同一类型的 Option,则返回 True。否则,返回 False。
示例
assert (Some(1) == Some(1)) is True
assert (Some(1) == Some(2)) is False
assert (Some(1) == Nothing()) is False
assert (Some(1) == 1) is False
Option.ne
Option.__ne__(self, other: Any) -> bool
启用使用!=
的不等性检查。
将 Option 与 other
进行比较。如果 other
是与具有相同包装值的同一类型的 Option,则返回 False。否则,返回 True。
示例
assert (Some(1) != Some(1)) is False
assert (Some(1) != Some(2)) is True
assert (Some(1) != Nothing()) is True
assert (Some(1) != 1) is True
Option.str
Option.__str__(self) -> str
启用使用str()
的有用的字符串表示。
示例
assert str(Some(1)) == "Some(1)"
assert str(Nothing()) == "Nothing()"
Option.repr
Option.__repr__(self) -> str
启用使用repr()
的有用的字符串表示。
示例
assert repr(Some(1)) == "Some(1)"
assert repr(Nothing()) == "Nothing()"
性能
可以使用 make bench
运行基准测试。在 bench/
中提供了基准测试工具。
目前,sample.py
基准测试定义了两个数据存储,一个使用经典的 Python 错误处理(或缺乏错误处理),另一个使用此库的包装类型。使用每个数据存储执行一些简单的操作以进行比较。
runner.sh
以两种方式运行基准测试。首先,它使用 hyperfine 将基准测试作为普通的 Python 脚本运行 100 次,并显示运行时间信息。然后,它使用 Python 的内置模块 timeit 来测量代码执行时间,在独立的情况下进行一百万次运行,无需启动解释器来解析和运行脚本。
结果
Result
和 Option
包装类型对执行时间的开销最小,对于大多数实际工作负载来说不会引起注意。然而,如果在“热点路径”中使用这些类型,则需要小心。
独立运行时,使用 Result
和 Option
类型的示例代码比内置异常处理慢约六倍
方法 | 执行次数 | 平均执行时间 | 与经典相比 |
---|---|---|---|
经典 | 1,000,000 (1E6) | 3.79E-6 s (3.79 μs) | 1x |
包装 | 1,000,000 (1E6) | 2.31E-5 s (23.1 μs) | 6.09x |
当作为 Python 脚本的一部分运行时,使用这些包装类型与使用内置异常处理和嵌套 if 语句的代码之间没有显著差异。
方法 | 执行次数 | 平均执行时间 | 与经典相比 |
---|---|---|---|
经典 | 100 | 32.2 ms | 1x |
包装 | 100 | 32.5 ms | 1.01x |
讨论
为了使此库中的包装类型尽可能高效,已采取了一些措施。所有类型都使用 __slots__
以避免为实例变量分配字典,并且包装变体(例如,Ok
和 Err
对于 Result
)被实现为 Result
的独立子类,而不是共享的类,以避免执行 if/else 分支或 isinstance()
检查,这在 Python 中尤其缓慢。
话虽如此,使用这些类型 确实是 做了比内置错误处理更多的事情!正在构造实例并访问方法。这两者在 Python 中相对较快,但肯定没有不做任何事情快,因此这个库可能永远不会像原始异常处理那样高效。但话虽如此,这并不是它的目标!目标是尽可能快,最好在常规旧 Python 习惯用法附近,同时提供关于处理错误和缺失数据的显著更佳的易用性和类型安全性。
贡献
欢迎贡献!要开始,您只需在本地上安装 Python 3。
一旦您已分叉并克隆了存储库,您可以运行
make test
- 使用您本地的解释器运行测试make fmt
- 使用 black 格式化代码make lint
- 使用各种分析工具检查代码make bench
- 运行基准测试
有关其他命令,请参阅 Makefile
。
CI 系统要求在合并代码之前 make lint
和 make test
必须成功运行(退出状态为 0)。
result_types
与 Python >= 3.6 兼容。您可以使用 make test-all-versions
运行所有支持的 Python 版本。这需要在您的本地系统上安装 docker
。或者,如果您已安装所有必需的 Python 版本,则可以运行 make tox
来在您的本地解释器上运行。
项目详情
下载文件
下载适用于您平台文件的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。