Python模式匹配
项目描述
高性能Python模式匹配和对象验证
用于Python的可复用模式匹配,由Cython实现。我最初为Ibis项目开发了这个系统,但希望它对其他人也有用。
实现旨在尽可能快,纯Python实现已经相当快,但利用Cython可以减轻Python解释器的开销。我也尝试了PyO3,但它的开销比Cython大。当前的实现使用Cython的纯Python模式,允许快速迭代和测试,然后可以将其Cython化并编译为扩展模块,从而显著提高速度。基准测试显示,与用Rust编写的pydantic模型验证相比,速度提高了2倍以上。
安装
该软件包已发布到PyPI,因此可以使用pip安装
pip install koerce
库组件
该库包含三个主要组件,可以独立使用或一起使用
1. 延迟对象构建器
这些允许延迟评估给定上下文中的Python表达式
In [1]: from koerce import var, resolve
In [2]: a, b = var("a"), var("b")
In [3]: expr = (a + 1) * b["field"]
In [4]: expr
Out[4]: (($a + 1) * $b['field'])
In [5]: resolve(expr, {"a": 2, "b": {"field": 3}})
Out[5]: 9
延迟对象提供的语法糖允许以简洁和自然的方式定义复杂对象转换。
2. 操作各种Python对象的模式匹配器
模式是该库的核心,它们允许在Python对象中搜索和替换特定的结构。该库提供了一种可扩展且简单的方法来定义模式和将值与其匹配。
In [1]: from koerce import match, NoMatch, Anything
In [2]: context = {}
In [3]: match([1, 2, 3, int, "a" @ Anything()], [1, 2, 3, 4, 5], context)
Out[3]: [1, 2, 3, 4, 5]
In [4]: context
Out[4]: {'a': 5}
请注意,可以使用from koerce import koerce
函数代替match()
,以避免与内置Python match
混淆。
from dataclasses import dataclass
from koerce import Object, match
@dataclass
class B:
x: int
y: int
z: float
match(Object(B, y=1, z=2), B(1, 1, 2))
# B(x=1, y=1, z=2)
其中,Object
模式检查传递的对象是否是B
的实例,以及value.y == 1
和value.z == 2
(忽略x
字段)。
模式还可以捕获值作为变量,使匹配过程更加灵活。
from koerce import var
x = var("x")
# `+x` means to capture that object argument as variable `x`
# then the `z` argument must match that captured value
match(Object(B, +x, z=x), B(1, 2, 1))
# it is a match because x and z are equal: B(x=1, y=2, z=1)
match(Object(B, +x, z=x), B(1, 2, 0))
# is is a NoMatch because x and z are unequal
模式也适用于匹配和替换任务,因为它们可以生成新的值。
# >> operator constructs a `Replace` pattern where the right
# hand side is a deferred object
match(Object(B, +x, z=x) >> (x, x + 1), B(1, 2, 1))
# result: (1, 2)
模式还可以组合使用,可以通过重载运算符自由组合。
In [1]: from koerce import match, Is, Eq, NoMatch
In [2]: pattern = Is(int) | Is(str)
...: assert match(pattern, 1) == 1
...: assert match(pattern, "1") == "1"
...: assert match(pattern, 3.14) is NoMatch
In [3]: pattern = Is(int) | Eq(1)
...: assert match(pattern, 1) == 1
...: assert match(pattern, None) is NoMatch
模式也可以从Python类型提示中构建。
In [1]: from koerce import match
In [2]: class Ordinary:
...: def __init__(self, x, y):
...: self.x = x
...: self.y = y
...:
...:
...: class Coercible(Ordinary):
...:
...: @classmethod
...: def __coerce__(cls, value):
...: if isinstance(value, tuple):
...: return Coercible(value[0], value[1])
...: else:
...: raise ValueError("Cannot coerce value to Coercible")
...:
In [3]: match(Ordinary, Ordinary(1, 2))
Out[3]: <__main__.Ordinary at 0x105194fe0>
In [4]: match(Ordinary, (1, 2))
Out[4]: koerce.patterns.NoMatch
In [5]: match(Coercible, (1, 2))
Out[5]: <__main__.Coercible at 0x109ebb320>
模式创建逻辑还通过轻量级类型参数推理处理泛型类型。实现相当紧凑,可在Pattern.from_typehint()
下使用。
3. 数据类对象的高级别验证系统
这种抽象类似于attrs或pydantic提供的内容,但也有一些区别(待列出)。
In [1]: from typing import Optional
...: from koerce import Annotable
...:
...:
...: class MyClass(Annotable):
...: x: int
...: y: float
...: z: Optional[list[str]] = None
...:
In [2]: MyClass(1, 2.0, ["a", "b"])
Out[2]: MyClass(x=1, y=2.0, z=['a', 'b'])
In [3]: MyClass(1, 2, ["a", "b"])
Out[3]: MyClass(x=1, y=2.0, z=['a', 'b'])
In [4]: MyClass("invalid", 2, ["a", "b"])
Out[4]: # raises validation error
可注解对象默认是可变的,但可以通过将immutable=True
传递给Annotable
基类来使其不可变。通常,将不可变对象也变为可散列也是很有用的,这可以通过将hashable=True
传递给Annotable
基类来实现,在这种情况下,散列值在初始化期间预先计算并存储在对象中,这使得字典查找变得廉价。
In [1]: from typing import Optional
...: from koerce import Annotable
...:
...:
...: class MyClass(Annotable, immutable=True, hashable=True):
...: x: int
...: y: float
...: z: Optional[tuple[str, ...]] = None
...:
In [2]: a = MyClass(1, 2.0, ["a", "b"])
In [3]: a
Out[3]: MyClass(x=1, y=2.0, z=('a', 'b'))
In [4]: a.x = 2
AttributeError: Attribute 'x' cannot be assigned to immutable instance of type <class '__main__.MyClass'>
In [5]: {a: 1}
Out[5]: {MyClass(x=1, y=2.0, z=('a', 'b')): 1}
可用的模式匹配器
这是一个不完整的匹配器列表,更多细节和示例请参阅koerce/patterns.py
和koerce/tests/test_patterns.py
。
Anything
和 Nothing
In [1]: from koerce import match, Anything, Nothing
In [2]: match(Anything(), "a")
Out[2]: 'a'
In [3]: match(Anything(), 1)
Out[3]: 1
In [4]: match(Nothing(), 1)
Out[4]: koerce._internal.NoMatch
Eq
用于相等匹配
In [1]: from koerce import Eq, match, var
In [2]: x = var("x")
In [3]: match(Eq(1), 1)
Out[3]: 1
In [4]: match(Eq(1), 2)
Out[4]: koerce._internal.NoMatch
In [5]: match(Eq(x), 2, context={"x": 2})
Out[5]: 2
In [6]: match(Eq(x), 2, context={"x": 3})
Out[6]: koerce._internal.NoMatch
Is
用于实例匹配
以下是一些简单的示例
In [1]: from koerce import match, Is
In [2]: class A: pass
In [3]: match(Is(A), A())
Out[3]: <__main__.A at 0x1061070e0>
In [4]: match(Is(A), "A")
Out[4]: koerce._internal.NoMatch
In [5]: match(Is(int), 1)
Out[5]: 1
In [6]: match(Is(int), 3.14)
Out[6]: koerce._internal.NoMatch
In [7]: from typing import Optional
In [8]: match(Is(Optional[int]), 1)
Out[8]: 1
In [9]: match(Is(Optional[int]), None)
泛型类型也通过检查属性/属性的类型得到支持
from koerce import match, Is, NoMatch
from typing import Generic, TypeVar, Any
from dataclasses import dataclass
T = TypeVar("T", covariant=True)
S = TypeVar("S", covariant=True)
@dataclass
class My(Generic[T, S]):
a: T
b: S
c: str
MyAlias = My[T, str]
b_int = My(1, 2, "3")
b_float = My(1, 2.0, "3")
b_str = My("1", "2", "3")
# b_int.a must be an instance of int
# b_int.b must be an instance of Any
assert match(My[int, Any], b_int) is b_int
# both b_int.a and b_int.b must be an instance of int
assert match(My[int, int], b_int) is b_int
# b_int.b should be an instance of a float but it isn't
assert match(My[int, float], b_int) is NoMatch
# now b_float.b is actually a float so it is a match
assert match(My[int, float], b_float) is b_float
# type aliases are also supported
assert match(MyAlias[str], b_str) is b_str
As
模式尝试将值强制转换为给定的类型
from koerce import match, As, NoMatch
from typing import Generic, TypeVar, Any
from dataclasses import dataclass
class MyClass:
pass
class MyInt(int):
@classmethod
def __coerce__(cls, other):
return MyInt(int(other))
class MyNumber(Generic[T]):
value: T
def __init__(self, value):
self.value = value
@classmethod
def __coerce__(cls, other, T):
return cls(T(other))
assert match(As(int), 1.0) == 1
assert match(As(str), 1.0) == "1.0"
assert match(As(float), 1.0) == 1.0
assert match(As(MyClass), "myclass") is NoMatch
# by implementing the coercible protocol objects can be transparently
# coerced to the given type
assert match(As(MyInt), 3.14) == MyInt(3)
# coercible protocol also supports generic types where the `__coerce__`
# method should be implemented on one of the base classes and the
# type parameters are passed as keyword arguments to `cls.__coerce__()`
assert match(As(MyNumber[float]), 8).value == 8.0
As
和 Is
可以省略,因为 match()
尝试使用 koerce.pattern()
函数将其第一个参数转换为模式
from koerce import pattern, As, Is
assert pattern(int, allow_coercion=False) == Is(int)
assert pattern(int, allow_coercion=True) == As(int)
assert match(int, 1, allow_coercion=False) == 1
assert match(int, 1.1, allow_coercion=False) is NoMatch
# lossy coercion is not allowed
assert match(int, 1.1, allow_coercion=True) is NoMatch
# default is allow_coercion=False
assert match(int, 1.1) is NoMatch
As[typehint]
和 Is[typehint]
可以用于创建模式
from koerce import Pattern, As, Is
assert match(As[int], '1') == 1
assert match(Is[int], 1) == 1
assert match(Is[int], '1') is NoMatch
If
模式用于条件表达式
允许基于对象的值或其他上下文中的变量进行条件匹配
from koerce import match, If, Is, var, NoMatch, Capture
x = var("x")
pattern = Capture(x) & If(x > 0)
assert match(pattern, 1) == 1
assert match(pattern, -1) is NoMatch
Custom
用于用户定义的匹配逻辑
传递给 match()
或 pattern()
的函数被视为 Custom
模式
from koerce import match, Custom, NoMatch, NoMatchError
def is_even(value):
if value % 2:
raise NoMatchError("Value is not even")
else:
return value
assert match(is_even, 2) == 2
assert match(is_even, 3) is NoMatch
Capture
用于在上下文中记录值
捕获模式可以以多种方式定义
from koerce import Capture, Is, var
x = var("x")
Capture("x") # captures anything as "x" in the context
Capture(x) # same as above but using a variable
Capture("x", Is(int)) # captures only integers as "x" in the context
Capture("x", Is(int) | Is(float)) # captures integers and floats as "x" in the context
"x" @ Is(int) # syntax sugar for Capture("x", Is(int))
+x # syntax sugar for Capture(x, Anything())
from koerce import match, Capture, var
# context is a mutable dictionary passed along the matching process
context = {}
assert match("x" @ Is(int), 1, context) == 1
assert context["x"] == 1
Replace
用于替换匹配的值
允许用新值替换匹配的值
from koerce import match, Replace, var
x = var("x")
pattern = Replace(Capture(x), x + 1)
assert match(pattern, 1) == 2
assert match(pattern, 2) == 3
存在 Replace
模式的语法糖,上面的示例可以写成
from koerce import match, Replace, var
x = var("x")
assert match(+x >> x + 1, 1) == 2
assert match(+x >> x + 1, 2) == 3
替换模式在匹配对象时特别有用
from dataclasses import dataclass
from koerce import match, Replace, var, namespace
x = var("x")
@dataclass
class A:
x: int
y: int
@dataclass
class B:
x: int
y: int
z: float
p, d = namespace(__name__)
x, y = var("x"), var("y")
# if value is an instance of A then capture A.0 as x and A.1 as y
# then construct a new B object with arguments x=x, y=1, z=y
pattern = p.A(+x, +y) >> d.B(x=x, y=1, z=y)
value = A(1, 2)
expected = B(x=1, y=1, z=2)
assert match(pattern, value) == expected
替换也可以用于嵌套结构中
from koerce import match, Replace, var, namespace, NoMatch
@dataclass
class Foo:
value: str
@dataclass
class Bar:
foo: Foo
value: int
p, d = namespace(__name__)
pattern = p.Bar(p.Foo("a") >> d.Foo("b"))
value = Bar(Foo("a"), 123)
expected = Bar(Foo("b"), 123)
assert match(pattern, value) == expected
assert match(pattern, Bar(Foo("c"), 123)) is NoMatch
SequenceOf
/ ListOf
/ TupleOf
from koerce import Is, NoMatch, match, ListOf, TupleOf
pattern = ListOf(str)
assert match(pattern, ["foo", "bar"]) == ["foo", "bar"]
assert match(pattern, [1, 2]) is NoMatch
assert match(pattern, 1) is NoMatch
MappingOf
/ DictOf
/ FrozenDictOf
from koerce import DictOf, Is, match
pattern = DictOf(Is(str), Is(int))
assert match(pattern, {"a": 1, "b": 2}) == {"a": 1, "b": 2}
assert match(pattern, {"a": 1, "b": "2"}) is NoMatch
模式列表
from koerce import match, NoMatch, SomeOf, ListOf, pattern
four = [1, 2, 3, 4]
three = [1, 2, 3]
assert match([1, 2, 3, SomeOf(int, at_least=1)], four) == four
assert match([1, 2, 3, SomeOf(int, at_least=1)], three) is NoMatch
integer = pattern(int, allow_coercion=False)
floating = pattern(float, allow_coercion=False)
assert match([1, 2, *floating], [1, 2, 3]) is NoMatch
assert match([1, 2, *floating], [1, 2, 3.0]) == [1, 2, 3.0]
assert match([1, 2, *floating], [1, 2, 3.0, 4.0]) == [1, 2, 3.0, 4.0]
模式映射
from koerce import match, NoMatch, Is, As
pattern = {
"a": Is(int),
"b": As(int),
"c": Is(str),
"d": ListOf(As(int)),
}
value = {
"a": 1,
"b": 2.0,
"c": "three",
"d": (4.0, 5.0, 6.0),
}
assert match(pattern, value) == {
"a": 1,
"b": 2,
"c": "three",
"d": [4, 5, 6],
}
assert match(pattern, {"a": 1, "b": 2, "c": "three"}) is NoMatch
可注解对象
可注解对象与数据类类似,但有一些不同
- 可注解对象默认是可变的,但可以通过将
immutable=True
传递给Annotable
基类来使其不可变。 - 可以通过将
hashable=True
传递给Annotable
基类来使可注解对象可散列,在这种情况下,散列值在初始化期间预先计算并存储在对象中,这使得字典查找变得廉价。 - 可以通过传递
allow_coercion=False
来控制验证严格性。当allow_coercion=True
时,注释被视为As
模式,允许将值强制转换为给定的类型。当allow_coercion=False
时,注释被视为Is
模式,并且值必须正好是给定的类型。默认值为allow_coercion=True
。 - 可注解对象支持继承,注释从基类继承,并合并签名,提供无缝体验。
- 可注解对象可以使用位置参数或关键字参数或两者一起调用,位置参数按照顺序与注释匹配,关键字参数按名称与注释匹配。
from typing import Optional
from koerce import Annotable
class MyBase(Annotable):
x: int
y: float
z: Optional[str] = None
class MyClass(MyBase):
a: str
b: bytes
c: tuple[str, ...] = ("a", "b")
x: int = 1
print(MyClass.__signature__)
# (y: float, a: str, b: bytes, c: tuple = ('a', 'b'), x: int = 1, z: Optional[str] = None)
print(MyClass(2.0, "a", b"b"))
# MyClass(y=2.0, a='a', b=b'b', c=('a', 'b'), x=1, z=None)
print(MyClass(2.0, "a", b"b", c=("c", "d")))
# MyClass(y=2.0, a='a', b=b'b', c=('c', 'd'), x=1, z=None)
print(MyClass(2.0, "a", b"b", c=("c", "d"), x=2))
# MyClass(y=2.0, a='a', b=b'b', c=('c', 'd'), x=2, z=None)
print(MyClass(2.0, "a", b"b", c=("c", "d"), x=2, z="z"))
# MyClass(y=2.0, a='a', b=b'b', c=('c', 'd'), x=2, z='z')
MyClass()
# TypeError: missing a required argument: 'y'
MyClass(2.0, "a", b"b", c=("c", "d"), x=2, z="z", invalid="invalid")
# TypeError: got an unexpected keyword argument 'invalid'
MyClass(2.0, "a", b"b", c=("c", "d"), x=2, z="z", y=3.0)
# TypeError: multiple values for argument 'y'
MyClass("asd", "a", b"b")
# ValidationError
性能
koerce
的性能至少与 pydantic
相当。由于使用 PyO3
绑定,pydantic-core
是用 Rust 编写的,使其成为一个性能相当不错的库。还有一个来自 Jim Crist-Harif
的更快验证/序列化库,称为 msgspec,它是直接使用 Python 的 C API 手工编写的 C 语言代码。
koerce
与 pydantic
或 msgpec
不完全相同,但它们是良好的基准测试候选者。
koerce/tests/test_y.py::test_pydantic PASSED
koerce/tests/test_y.py::test_msgspec PASSED
koerce/tests/test_y.py::test_annotated PASSED
------------------------------------------------------------------------------------------- benchmark: 3 tests ------------------------------------------------------------------------------------------
Name (time in ns) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_msgspec 230.2801 (1.0) 6,481.4200 (1.60) 252.1706 (1.0) 97.0572 (1.0) 238.1600 (1.0) 5.0002 (1.0) 485;1616 3,965.5694 (1.0) 20000 50
test_annotated 525.6401 (2.28) 4,038.5600 (1.0) 577.7090 (2.29) 132.9966 (1.37) 553.9799 (2.33) 34.9300 (6.99) 662;671 1,730.9752 (0.44) 20000 50
test_pydantic 1,185.0201 (5.15) 6,027.9400 (1.49) 1,349.1259 (5.35) 320.3790 (3.30) 1,278.5601 (5.37) 75.5100 (15.10) 1071;1424 741.2206 (0.19) 20000 50
我尝试使用 msgspec
和 pydantic
中性能最好的 API,将参数作为字典接收。
我计划进行更彻底的比较,但 koerce
的类似模型的注解 API 的性能大约是 pydantic
的两倍,但大约是 msgspec
的一半。考虑到实现方式,这也很有道理,因为 PyO3
可能比 Cython
有更高的开销,但它们都无法与手工编写的 Python C-API
代码的性能相媲美。
这种性能结果可以略有改善,但有两个巨大的优势是其他两个库所没有的。
- 它使用纯 Python 和 Cython 装饰器实现,因此即使不编译它也可以使用。它还可以启用 JIT 编译器,如 PyPy 或 CPython 3.13 带来的新复制和修补 JIT 编译器,以更好地优化热点路径。
- 完全在 Python 中开发使其更容易贡献。不需要学习 Rust 或 Python 的 C API 就可以修复错误或贡献新功能。
待办事项
README 正在建设中,计划进行改进
- 使用 @annotated 装饰器验证函数的示例
- 解释
allow_coercible
标志 - 为每个模式提供适当的错误消息
开发
- 该项目使用
poetry
进行依赖管理和打包。 - Python 版本支持遵循 https://numpy.com.cn/neps/nep-0029-deprecation_policy.html
- 轮子是通过使用
cibuildwheel
项目构建的。 - 实现是纯 Python 和 Cython 注释。
- 该项目使用
ruff
进行代码格式化。 - 该项目使用
pytest
进行测试。
开发者指南将很快提供。
参考文献
该项目主要受到以下项目的启发
项目详细信息
下载文件
下载您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源分布
构建分布
koerce-0.5.1.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 20db6412e7e2aed1b7ce893307152333f88d0313ba3bc91e0582756acea45bbe |
|
MD5 | 93204e46af4011368c0800a8be842bde |
|
BLAKE2b-256 | 53003d1a70f4cbdb51f91c1a01295e0889e992ca67b35bca8c2fc030c4a61a8b |
koerce-0.5.1-cp312-cp312-win_amd64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 91d00ab89656a32cf669c2394595c89c8b147a2bc87d30ccc983e4c9cfa54a52 |
|
MD5 | 7a42870f47a42d159e74fd8532ed13cd |
|
BLAKE2b-256 | 4391a438626128a0bb3a88d77bab60fa1e8bdd0ecaec8e6e6a658b8c2989a52e |
koerce-0.5.1-cp312-cp312-win32.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | de99f796f11490f9c900c1cc7b952e45973719007300e8a2de6f8149252bfb77 |
|
MD5 | b16caae6531052f747a83cf2391013a5 |
|
BLAKE2b-256 | 197334866663ddf39d6b3ea5db4f822dc950894c9eb2764ee7eb14b423fd6359 |
koerce-0.5.1-cp312-cp312-musllinux_1_2_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 64e94be035ec3b115576ed258ed293a7a81f074eea23108e8c2b2bc1482501da |
|
MD5 | c21e21dd1d27bc261bbab78345c5d4b3 |
|
BLAKE2b-256 | 01d909a876e909a479345530ab40724bb0907fcfc21548a2841543ac321ea82e |
koerce-0.5.1-cp312-cp312-musllinux_1_2_i686.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 03f0b03fa961c8f49d687ad351ea16b60878d09f5b3f36fc56b5f196f8d40db8 |
|
MD5 | be0984582f352c62c0f8ba7752db4427 |
|
BLAKE2b-256 | 7aa01eba9dfb0c9f02199ab44ca5224b7291fa5759a27b5fb6f5e97b554340fb |
koerce-0.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | d924d9df9e8de3129bb8b4083d63ac246a4cd4fb4eb5af5c181c324fac9d50c3 |
|
MD5 | 50d88a73f85b1e81df015cd166fcc81c |
|
BLAKE2b-256 | d3f5c18db790fafb964982bca5914f3b4b840b837fd3e073340e8dd63da9152b |
koerce-0.5.1-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | d545eb05a33544e56df4c9ba1e780cba1eca9fcf5f7e4300d02922ce7ebadaab |
|
MD5 | 642279b88078b9f247966de184dd83f3 |
|
BLAKE2b-256 | e88ea2365ccceaea10afeb0b5d3bccfa6646595ee377ef78ead0acf023018544 |
koerce-0.5.1-cp312-cp312-macosx_14_0_arm64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 813aa591e1b2dbfacef7f3311243b58a7be26115922d437e4c303d80aec56dcc |
|
MD5 | 76e91482554127ea92a2c280945aa56b |
|
BLAKE2b-256 | ed4d4c2a05f0066639ccccb68e9a0e20d8e30f70de76d752fd80b8e9ef997f30 |
koerce-0.5.1-cp312-cp312-macosx_13_0_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 95b12440c2e00c90563fa327d70781f290d17602b9c71150f4d2541aae1b9744 |
|
MD5 | e4a4e1e0331b16a1c269c92a3cd6b100 |
|
BLAKE2b-256 | 21b6c1baa93b22fbf10d46deaa1019b4438191fe3da757b7f697a255f3720740 |
koerce-0.5.1-cp311-cp311-win_amd64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 38cc10a6e3480a51e1557788de570cdc42ff8c5cf83de4bc856062827f1e087c |
|
MD5 | 60751dc68417e5a5278fcb0dd83daf9b |
|
BLAKE2b-256 | bd370e33e9c868750da2df80e6a2a1fe22b3cea5324b9c85d27c6f05f1451b47 |
哈希值 用于 koerce-0.5.1-cp311-cp311-musllinux_1_2_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 942ab409d7c4930519310544f31f1446e2f4ac059415feffe2a811a52435b7c3 |
|
MD5 | 29f88d46c6aba42a9564b2439c36fba5 |
|
BLAKE2b-256 | 9b89b4b95a755fb79ca9bb7c684e03d979f341b0294c770ba23be9b08a893a69 |
哈希值 用于 koerce-0.5.1-cp311-cp311-musllinux_1_2_i686.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 03a133958b669c5a3dd7c08de6dc3b1c72d204e633957ce3c40e49357e36406e |
|
MD5 | 13a3eb3dd6db5ea3a5967490f088288b |
|
BLAKE2b-256 | fe9c9f1ea6a7d2bda9383124bcf702593688f7140c2a38b658826dc7e07e6e8d |
哈希值 用于 koerce-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a8ffbf5c66c0208a6b08cfd758102ec39fb89c0b1ff82c91ccde20396e013e18 |
|
MD5 | 60a402a54bfc5d391c9c23b19b046d30 |
|
BLAKE2b-256 | 867dd10201436b13f7b440c707889a24e85921bcd5c4f5434ec137a3029040c5 |
哈希值 用于 koerce-0.5.1-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | e00c4aaebc35c0c7c5e65a0f6ab76428ede0eda291baa5146512da505feb1397 |
|
MD5 | 00b3000ff1f9557f0b75544c0b657db8 |
|
BLAKE2b-256 | 19d880c12e9142a029fc34e261665a77410350212b0a0d57c89b435aaa46a9f2 |
哈希值 用于 koerce-0.5.1-cp311-cp311-macosx_14_0_arm64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3bf30dd4b8875b69c2322a370fbe52267ff0f3291250ba56f26f39903a378d4d |
|
MD5 | 5aef3c66f297add3f45ab5ebabfa1183 |
|
BLAKE2b-256 | 75bc34fa6fd0a10b82fe2430edbc4ecfe8702732cd09e92e80d8af79168b84e8 |
哈希值 用于 koerce-0.5.1-cp311-cp311-macosx_13_0_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a05d31e8fed9451dedc4125736fdcfbe7e8f4ee6a42697ffb65dff791180a00a |
|
MD5 | cf1648b08c20af24aa60d004fc903bdf |
|
BLAKE2b-256 | a7217d08a92a16676c4007ab71bde6c7672a74b5cc6a0dfa2d389c4840e4e265 |
哈希值 用于 koerce-0.5.1-cp310-cp310-musllinux_1_2_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f0e58cf2c40df250ea7daf5bc8e0acdb022c0ce6eaaa7de6b3075cbaab1bd7b7 |
|
MD5 | 68e9b888a92e1af0e255ee0653999a9f |
|
BLAKE2b-256 | 0bc5a50ba13d42dadf265401974759e9e7fdafc3e5870c76c67ded322f08be1c |
哈希值 用于 koerce-0.5.1-cp310-cp310-musllinux_1_2_i686.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 050238a030dd8726bec33890d79a2cbc1e5733f01438a7d37d7bf3566608ba63 |
|
MD5 | 4f2e0bc3a90ace01d52992d297d4d838 |
|
BLAKE2b-256 | 824f7c3aff513d58d085e63abd327fccefec5c423d3572268b1a47b98e8136ad |
哈希值 用于 koerce-0.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | b6742b840ab1feb80a31a086a3f24d64899ba9fb73466782c98b0df74231dfdf |
|
MD5 | f0aeb435548362291bf0b9a07db02396 |
|
BLAKE2b-256 | 55a1c5f42f82fc4235aa6b1e3ecd6c4dc86166799ab7860be6616e9ed698ab7b |
哈希值 用于 koerce-0.5.1-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bac1a35126533320f569459b9d47e651b3053fcf8be4d2a8f0215b9aaaa54df6 |
|
MD5 | 76ea7bdd803975345bd0265d5099b244 |
|
BLAKE2b-256 | c47e65e5ee84df381a8d884d0d792a51c078fdbd48573f990271b9faa287343a |
哈希值 用于 koerce-0.5.1-cp310-cp310-macosx_14_0_arm64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3c4cd9d54194ef7692c893be44e4aac1f471cf8bc952004a68016f9303b2ecc4 |
|
MD5 | 77a5bc860297e2556cdff1b41819a771 |
|
BLAKE2b-256 | 7042d45aa82dc3767601466e532524ee28c5115838f43d4e5379d26ad1ed787e |
哈希值 用于 koerce-0.5.1-cp310-cp310-macosx_13_0_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 53ca3c48efe1848c68b6a0ff71d6b962873fae32d017ac73ad2d717ff65b8a7a |
|
MD5 | 21e6357675ff1dfad3f80ebdc659140a |
|
BLAKE2b-256 | 2a3b701d9aa4245f8839e42d839b1804e25ffcc1a240044158851995300b64e6 |