用于在python中定义不可变和类型的namedtuple样式库。

sumtype的Python项目详细描述


sumtype

一个namedtuple样式库,用于在python中定义不可变的sum类型。(Get it on PyPI

You may know sum types under a different name – they're also referred to as tagged unions, enums in Rust/Swift, and variants in C++. If you haven't heard about them yet, here's a nice introduction.

目前的版本是0.10.0,很快接近1.0。 库支持Python3.x。 核心代码已经存在于各种文件夹中大约一年了, 在我厌倦复制之前,我决定把它作为一个独立的包发布。 (另请参见:Should I use it?

欢迎提出建议、反馈和意见!

快速游览

>>>fromsumtypeimportsumtype>>>fromtypingimportTuple>>>>>>classThing(sumtype):...defFoo(x:int,y:int):......defBar(y:str,hmm:Tuple[str,str]):......defZap():......>>>

这意味着Thing值可以是三个变量之一:

  • 带两个int字段的xy
  • str字段yTuple[str, str]字段hmm
  • 没有字段的Zap

如果提供了类型注释,构造函数将对参数进行类型检查(请参见Typechecking) 您还可以在类定义中添加自己的docstring和方法。 如果您喜欢namedtuple样式的定义,sumtype也支持这些定义-请参见sumtype.sumtype.demo()中的Thing2示例。

创建值和属性访问
>>>foo=Thing.Foo(x=3,y=5)# named arguments>>>bar=Thing.Bar('hello',('wo','rld'))# positional arguments>>>zap=Thing.Zap()

注意,它们仍然只是同一类型的不同值,而不是子类:

>>>type(foo)isThingandtype(bar)isThingandtype(zap)isThingTrue

每个指定字段都有一个访问器:

>>>foo.x,foo.y;(3,5)>>>bar.y,bar.hmm('hello',('wo','rld'))

…检查访问是否有效,并显示描述性错误消息:

>>>Thing.Zap().hmm# only `Bar`s have a `hmm` fieldTraceback(mostrecentcalllast):...AttributeError:Incorrect'Thing'variant:Field'hmm'notdeclaredinvariant'Zap'...>>>>>>Thing.Foo(x=1,y=2).blah_blah_blah# no variant has a `blah_blah_blah` field Traceback(mostrecentcalllast):...AttributeError:Unknownattribute:Field'blah_blah_blah'notdeclaredinanyvariantof'Thing'...

这些值还有一个很好的__repr__()

>>>foo;bar;zapThing.Foo(x=3,y=5)Thing.Bar(y='hello',hmm=('wo','rld'))Thing.Zap()

这个库的设计考虑到了效率——它使用__slots__来存储属性 并为每个类生成所有方法的专用版本。 要查看生成的代码,请执行class Thing(sumtype, verbose=True):

呃,至少我喜欢这样想;)不过,我尽量用轮廓来描绘事物!

功能

打字检查

sumtype使用^{}键入检查字段:

>>># Foo(x: int, y: int) -> Thing>>>Thing.Foo(x=1,y=2)Thing.Foo(x=1,y=2)>>>Thing.Foo(x='should be an int',y=2)Traceback(mostrecentcalllast):...TypeError:typeofargument"x"mustbeint;gotstrinstead

typing也支持注释:

>>># Bar(y: str, hmm: Tuple[str, str]) -> Thing>>>Thing.Bar(y='a',hmm=('b','c'))Thing.Bar(y='a',hmm=('b','c'))>>>Thing.Bar(y='a',hmm=(1,2))Traceback(mostrecentcalllast):...TypeError:typeofargument"hmm"[0]mustbestr;gotintinstead

^{}支持所有的typing结构(TupleListDictUnion等)。 (完整列表请参见他们的README) 但是,从2.2.2开始,它不支持用户定义的泛型类,因此对于像z: UserDefinedList[float]这样的字段,typeguard将不会检查 如果内容实际上是floats。 这也阻止我们定义泛型sumtype(例如ConsList[A]Maybe[A]Either[A, B]),但我正在解决这个问题。

类型检查可以用typecheck参数控制:class Thing(sumtype, typecheck='always'|'debug'|'never'):。 默认模式是'always' 不带注释的字段将不会被类型检查,您可以在定义中混合使用带注释和不带注释的字段。

相等和散列

>>>Thing.Foo(1,2)==Thing.Foo(1,2)True>>>Thing.Foo(1,2)==Thing.Bar('a',('b','c'));False>>>{foo,foo,bar,zap}=={foo,bar,zap}True

__eq____hash__注意变异-即使我们有变异Moo(x: int, y: int)Foo(1,2) != Moo(1,2)hash(Foo(1,2)) != hash(Moo(1,2))

Note: Just like tuples, sumtypes__eq__/__hash__ work by __eq__ing/__hash__ing the values inside, so the values must all implement the relevant method for it to work.

修改值

>>>foo;foo.replace(x=99)Thing.Foo(x=3,y=5)Thing.Foo(x=99,y=5)>>>>>>bar;bar.replace(y='abc',hmm=('d','e'))Thing.Bar(y='hello',hmm=('wo','rld'))Thing.Bar(y='abc',hmm=('d','e'))

foo.replace(x=99)返回一个新值,就像在namedtuple中一样。 .replace的行为就像构造函数w.r.t.类型检查一样。

Note: replace and all the other methods have underscored aliases (_replace). So even if you have a field called replace, you can still use my_value._replace(x=15).

模式匹配

声明格式:
>>>defdo_something(val:Thing):...ifval.is_Foo():...print(val.x*val.y)...elifval.is_Bar():...print('The result is',val.y,''.join(val.hmm))...elifval.is_Zap():...print('Whoosh!')...else:val.impossible()# throws an error - nice if you like having all cases covered...>>>forvalin(foo,bar,zap):...do_something(val)...15TheresultishelloworldWhoosh!
表达式形式:
>>>[val.match(...Foo=lambdax,y:x*y,...Zap=lambda:999,..._=lambda:-1# default case...)...forvalin(foo,bar,zap)][15,-1,999]

sumtypes与标准类型之间的转换

去…

>>>foo.values();foo.values_dict();(3,5)OrderedDict([('x',3),('y',5)])
>>>foo.as_tuple();foo.as_dict()('Foo',3,5)OrderedDict([('variant','Foo'),('x',3),('y',5)])

…从

>>>Thing.from_tuple(('Foo',10,15));Thing.from_dict({'variant':'Foo','x':10,'y':15})Thing.Foo(x=10,y=15)Thing.Foo(x=10,y=15)

还有x == Thing.from_tuple(x.as_tuple())x == Thing.from_dict(x.as_dict())

酸洗支架

>>>importpickle>>>vals=[Thing.Foo(1,2),Thing.Bar('one',('two','three')),Thing.Zap()]>>>vals2=pickle.loads(pickle.dumps(vals))>>>vals;vals==vals2[Thing.Foo(x=1,y=2),Thing.Bar(y='one',hmm=('two','three')),Thing.Zap()]True

sumtype.tests中也有一些测试,以确保一切正常工作。 这就是一切…现在!

1.0

中计划的功能
  • 以类型安全的方式定义像Maybe[A]/Either[A, B]这样的泛型sumtype

我应该用它吗?

是啊!我建这个图书馆并不是因为我觉得它会很好- 我正在开发一个应用程序和一些小项目中大量使用它。 说它是经过战斗测试的有点多,但它已经达到了目的。

未来可能的功能

  • 默认值

  • mypy支持。 不幸的是,上一次我检查时,mypy并没有很好地处理元类创建的类,但这可能已经改变了。 或者,我们可以提供一种生成mypy存根文件。而且,现在没有办法告诉mypy 访问器的返回类型取决于变量–Union[a, b]很接近,但是mypy会抱怨不是所有的情况 即使他们被处理。

  • 静态生成文件的类定义

  • 自定义生成方法的动态替代方案- 如果启动时间比效率更重要,则可能有用

  • 如果需要真正的不变性,另一个由元组支持的实现- 目前还没有办法让基于__slots__的实现在这方面做到无懈可击,我正在尽最大努力。

  • 可能opt-in-mutability–目前,如果需要,可以使用Thing._unsafe_set_Foo_x(foo, 10),但这不是一个好的接口

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java我能在这个程序中更好地使用内存吗?   为什么我的Java while循环迭代了一半   java IntelliJ IDEA不在构建时复制资源   socket仅在Java TCP服务器输出上检查客户端断开连接   java游戏物理摩擦   java片段onClick调用方法   symja数学分析器中无法识别java Abs[x]   java在使用泛型时创建二进制搜索树类的实例?   java在外键约束表上的添加和删除   语法java表达式的含义,如果有条件   java创建内联对象并作为参数传递   是否有相当于Redis排序集(zset)的Java数据结构   java找不到适合的方法(无参数)   音频文件操作给定字节帧的音量Java   Eclipse4不以JavaWebStart启动   java如何使用org在JSON对象中获取嵌套的键元素。json?   java与Jackson的反序列化:“org.codehaus.Jackson.map.JsonMappingException:无法反序列化[projectname]的实例。”   字符串的Java正则表达式   spring集成上的java检测缺火指令