FastCore:使用新功能扩展Python的库

2020-09-06 23:45:17

我最近开始了磨练我的蟒蛇技能的旅程:我想学习高级模式、习惯用法和技术。我从阅读关于高级Python的书籍开始,然而,如果没有地方应用它,这些信息似乎就不会被牢牢记住。我还希望能够在学习期间向专家提问--这是一种很难找到的安排!这就是我突然想到的问题:如果我能找到一个拥有相当高级的python代码并编写文档和测试的开放源码项目,会怎么样?我打赌,如果我这样做,将迫使我深入学习所有的东西,维护人员会欣赏我的工作,并愿意回答我的问题。

而这正是我在过去一个月里所做的!我很高兴地报告,这是我经历过的最有效的学习体验。我发现,编写文档不仅迫使我深入理解代码的作用,而且还迫使我深入理解代码的工作方式,并在编写测试的同时探索边缘案例。最重要的是,当我陷入困境时,我能够提出问题,并且维护人员愿意投入额外的时间,因为他们知道他们的指导是为了使他们的代码更容易访问!事实证明,我选择的库FastCore是我见过的最吸引人的Python之一,因为它的目的和目标相当独特。

对于外行来说,FastCore是一个库,许多fast.ai项目都建立在它的基础上。最重要的是,FastCore扩展了Python编程语言,并努力消除样板并为常见任务添加有用的功能。在这篇博客文章中,我将重点介绍FastCore提供的一些我最喜欢的工具,而不是分享我对Python的了解。我的目标是激发您对这个库的兴趣,并希望激励您在完成学习更多内容后查看文档!

不用离开Python就可以接触到其他语言的想法:我一直听说学习其他语言对成为一名更好的程序员是有益的。从实用的角度来看,我发现学习其他语言很难,因为我从来不会在工作中使用它们。Fastcore对python进行了扩展,包括了Julia、Ruby和Haskell等多种语言中的模式。现在我了解了这些工具,我有动力去学习其他语言。

您将获得一组新的实用工具:fast core包含的实用程序将允许您编写更简洁、更具表现力的代码,并可能解决新的问题。

了解有关Python编程语言的更多信息:因为FastCore扩展了Python编程语言,所以在此过程中会暴露出许多高级概念。对于有动力的人来说,这是一个很好的方式来了解Python的内部有多少工作。

每当我看到带有参数**kwargs的函数时,我都会有点畏缩。这是因为这意味着API是模糊的,我必须阅读源代码才能找出哪些是有效参数。请考虑以下示例:

Def baz(a,b=2,c=3,d=4):返回a+b+c def foo(c,a,**kwargs):返回c+baz(a,**kwargs)检查。签名(Foo)

如果不阅读源代码,我可能很难知道foo还接受附加参数b和d。我们可以使用委托来解决这个问题:

Def baz(a,b=2,c=3,d=4):return a+b+c@Delegations(Baz)#此修饰符将向下传递来自baz def foo(c,a,**kwargs)的关键字参数:return c+baz(a,**kwargs)check。签名(Foo)。

您可以自定义此装饰器的行为。例如,你可以通过传递你的论点并保留**kwargs,既可以拥有蛋糕,也可以吃蛋糕:

Def basefoo(a,b=2,c=3,d=4):PASS@Delegations(basefoo,BUT=[';d';])#exclude`d`def Foo(c,a,**kwargs):通过检查。签名(Foo)。

Class BaseFoo:def__init__(self,e,c=2):pass@Delegations()#因为这里没有传递参数,所以我们委托给超类foo(BaseFoo):def__init__(self,a,b=1,*kwargs):Super()。__init__(**kwargs)检查。签名(Foo)。

您是否想过是否有可能避免在__init__中设置属性所涉及的样板?

唉哟!。那太痛苦了。看看所有重复的变量名。在定义类时,我真的需要像这样重复自己吗?不再是了!结账存储属性(_A):

类测试:def__init__(self,a,b,c):store_attr(BUT=[';c';])t=Test(5,4,3)Assert。B==4 Assert not hasattr(t,';c';)

定制和使用store_attr的方式比我在这里强调的方式多得多。有关更多详细信息,请查看文档。

关于Python,我讨厌的一点是与子类化相关的__super__().__init__()样板。例如:

类ParentClass:def__init__(Self):self。Some_attr=';hello';class ChildClass(ParentClass):def__init__(Self):Super()。__init__()cc=ChildClass()assert cc。一些_attr=#hello&##您使用的是仅可使用的b/c。

我们可以通过使用元类PrePostInitMeta来避免此样板。我们定义了一个名为NewParent的新类,它是ParentClass的包装器:

类NewParent(ParentClass,metaclass=PrePostInitMeta):def__pre_init__(self,*args,**kwargs):Super()。__init__()class ChildClass(NewParent):def__init__(Self):pass sc=ChildClass()assert sc。一些_attr==';你好';

类型分派或多个分派允许您根据函数接收的输入类型更改函数的行为方式。这在某些编程语言(如Julia)中是一个突出的特性。例如,这是一个概念性示例,说明了多个分派在Julia中的工作方式,根据x和y的输入类型返回不同的值:

Collide_with(x::Asteroid,y::Asteroid)=...#处理小行星撞击小行星Collide_with(x::Asteroid,y::宇宙飞船)=...#处理小行星撞击宇宙飞船Collide_with(x::宇宙飞船,y::Asteroid)=...#处理宇宙飞船撞击小行星Collide_with(x::宇宙飞船,y::宇宙飞船)=...#处理宇宙飞船撞击宇宙飞船。

类型分派在数据科学中可能特别有用,在数据科学中,您可能允许对处理数据的函数使用不同的输入类型(即Numpy数组和Pandas数据帧)。类型分派允许您为执行类似任务的函数提供通用API。

不幸的是,Python不支持这种开箱即用的方式。幸运的是,有@TypeDispatch修饰符可以拯救。此修饰符依赖于类型提示,以便将输入路由到函数的正确版本:

此功能有其局限性,以及您可以在此处阅读到的使用此功能的其他方式。在学习类型化调度的过程中,我还发现了一个由Mathhew Rocklin(Dask的创建者)制作的名为MultipleDispatch的Python库。

使用此功能后,我现在有动力学习像Julia这样的语言,以发现我可能遗漏的其他范例。

Functools.part是一个很棒的实用程序,它可以从允许您设置默认值的其他函数创建函数。让我们以此函数为例,该函数过滤列表以仅包含值>;=val:

Test_input=[1,2,3,4,5,6]def(arr,val):";筛选列表以删除任何小于val的值。";return[x for x in arr if x>;=val]f(test_input,3)。

您可以使用将默认值设置为5的PARTIAL在此函数之外创建新函数:

PARTIAL的一个问题是它删除了原始文档字符串,并将其替换为通用文档字符串:

';Partial(func,*args,**关键字)-使用给定参数和关键字的部分应用程序的新函数。\n';

Fastcore.utils.partaller修复了这个问题,并确保保留文档字符串,以便新的API是透明的:

在函数式编程语言中普遍使用的一种技术是函数组合,通过它您可以将一组函数链接在一起以实现某种结果。这在应用各种数据转换时特别有用。考虑一个玩具示例,其中我有三个函数:(1)删除列表中小于5的元素(来自上一节)(2)将每个数字加2(3)对所有数字求和:

Def add(arr,val):return[x+val for x in arr]def arrsum(Arr):return sum(Arr)#参见上一节关于partaller add2=partaller(add,val=2)转换=Compose(filter5,add2,arrsum)转换([1,2,3,4,5,6])。

但是为什么这是有用的呢?你可能会想,我可以通过以下方式完成同样的任务:

你说得没错!但是,合成为您提供了一个方便的界面,以防您要执行以下操作:

Def fit(x,Transforms:List):";执行变换后拟合模型";x=Compose(*Transforms)(X)y=[np。均值(X)]*len(X)#这是个哑巴模型。不要判断我返回y#过滤出元素<;5,加2,然后预测平均拟合(x=[1,2,3,4,5,6],Transforms=[filter5,add2])。

在python中,__repr__可以帮助您获取有关对象的信息,以便进行日志记录和调试。下面是定义新类时默认获得的内容。(注意:我们使用的是store_attr,这在前面已经讨论过)。

用装饰器修补外部库会很方便,这在您想要修补要导入的外部库时特别有用。我们可以使用来自fast core.Foundation的装饰符@patch以及如下类型提示:

还没被说服?在下一节中,我将向您展示这种修补的另一个示例。

当您看到pathlib.path的这些扩展时,您就不会再使用普通的pathlib了!已向pathlib添加了许多其他方法,例如:

等等!这是怎么回事?我们刚刚导入了pathlib.Path-为什么我们要获得这个新功能?这是因为我们导入了fast core.Foundation模块,该模块通过前面讨论的@patch修饰器修补该模块。为了让大家明白为什么@patch修饰器很有用,我现在将继续向path添加另一个方法:

Self(大写为S)是创建调用对象方法的lambdas的更简明的方式。例如,让我们创建一个获取Numpy数组和的lambda:

进口熊猫为Pd df=Pd。DataFrame({';某些列';:[';a';,';a';,';b';,';b';,],';另一列';:[5,7,50,70]})f=SELF。Groupby(';某些列';)。平均值()f(Df)。

这些简单但方便,使您可以知道代码是否在Jupyter Notebook、Colab或IPython Shell中执行:

如果您要在代码中显示某些类型的可视化效果、进度条或动画,并且可能需要根据环境进行修改或切换,则此功能非常有用。

你可能对Python的列表很满意。这是一种你不知道自己需要一份更好的清单的情况,直到有人给你看了一份清单。输入L,这是一个类似列表的对象,有许多额外的好东西。

我能描述L的最好方式是假装List和Numpy有一个漂亮的孩子:

定义一个列表(查看显示列表长度的niceRepr__!)。

我还想向您展示更多关于FastCore的内容,但是它们不可能合理地放入一篇博客文章中。以下是我在这篇博客文章中没有演示的一些我最喜欢的东西的清单:

Utilites部分包含许多快捷方式,用于执行常见任务或为标准Python提供的内容提供附加接口。

在每个工作进程上成批处理事物,例如:如果您有一个向量化操作要以块为单位执行。

Transforms是用于创建数据转换和关联管道的实用程序集合。这些转换实用程序构建在本文中讨论的许多构建块之上。

需要注意的是,您应该先阅读文档的主页,然后阅读关于测试的部分,以全面理解文档。

这篇博客文章完全是在Jupyter笔记本上写的,GitHub自动将其转换为博客文章!听起来很有趣吧?查看快速页面。