用于在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无法在未设置数据源的情况下启动springboot应用程序   返回/泛型的类型?   java通过在navigationView中按id重新加载navigationView内容   java实现安卓的状态更新   java Equals()对于两个相等的字符串不返回true   java如何保存屏幕截图(matlab)   java GWT如何在重新加载页面之前确保函数已完全执行   java在Groovy中实现ObjectJSON映射的标准方法是什么?   java在ApacheTomcat中,是否可以通过连接器过滤多个访问日志文件?   java当JVM达到其Xmx限制时,它会强制垃圾收集吗?   如何在JAVA中生成包含特定数字的不同随机数列表?   rcp中透视图之间的java切换   java理解名为“分区”的Linkedlist算法中的无限循环   RestTemplate的java测微计统计信息   Android中使用自定义服务BLE的java读/写特性   java验证输入以确保负数   关于Java扫描器的io基本查询   java如何使用子字符串或其他函数将字符串拆分为单词?   java Storm群集重复元组