Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。
你用Python写过程序吗?那你一定用过attrs。
为什么?不要问,直接使用它。
好吧。让我们言归正传。
我爱Python;它是我使用过的第一门编程语言,迄今为止已经十几年了。虽然有些其他的一些有趣的编程语言,我始终没有想要更换编程语言的想法。
但是Python也不是十全十美的。在一些情况下,它促使你去做些错事。尤其是在一些类库中,一些类的继承关系的滥用和"上帝对象’及"反面模式’的滥用。
造成这个现象的一个原因可能是Python是一门易学的语言,所以一些缺乏经验的程序员犯了错,他们则不得不忍受下去。
但是我们认为一个更重要的原因是Python有时会因为你做了正确的事而惩罚你。
所谓正确的事就是对象设计过程中,设计一些小的,简单易用的类去专注做一件事情并且做好它。举个例子,如果你发现你的对象正在增加许多的私有方法,也许你应该把共有的方法作为私有方法的属性。如果你这么做了,你就不会感到困惑。
另外一个你可能定义对象的地方是当你有许多的相关数据需要他们之间的关系,不变量和行为解释。Python使定义一个元组或者一个列表及其容易。当你第一次输入host,port = ...去代替address = ...的时候,你不会感觉到什么不同,但是当你处处输入 [(family,socktype,proto,canonname,sockaddr)] = ... 你的生活充满了遗憾。这种情况下,你是幸运的。如果你是不幸的,你要时时维护那些像 value[0][7][HOSTNAME]["canonical"]的代码,此时,你的生活充满了痛苦,而不是那些更复杂的情感。
这样就提出了一个问题:在Python中使用类是否令人生厌?让我们看一个简单的数据结构:一个三维的笛卡尔坐标系。它从最简单开始:
又快又好。我们得到了一个三维点。接下来呢?
哇,这真是不幸。我只是需要一个存放一小部分数据的容器,却在不知不觉中复写了Python中的一个特殊内置函数。还不是太坏,毕竟所有的编程都是跟随潮流的文字符号嘛.
我看到了我的属性名,这也有所进展阿。
我已经说过我想要一个变量 x ,但是现在我不得不把它设置为一个属性...
只有 x 吗?很明显不是的。
现在我需要对我的每一个属性做一次初始化,所以这意味着我要输入每个属性名三次吗?
好吧。至少我是这么做的。
接下来你就会明白我做了什么。
来吧。所以我需要输入每个属性名五次。如果我想看我调试的到底诗歌是个什么东西,哪里可以找到一个元组让我来查看?
七次吗?
九次了阿?
好吧,多两行代码并没有什么。但是我们现在还没有定义其他比较的功能。现在我们完了,对吗?
你知道吗?我已经完了。20行代码,我们仍没有一个能够做任何事情的类;这个问题最困难的地方就是四分处理法,而不是做一个数据结构能够进行打印和比较。我陷入那些该死的元组,列表和字典中。定义一个数据结构在Python中太难了。
namedtuple的使用
标准库给出的解决方式是使用namedtuple。当它的初稿发布时(它同我的代码有许多相似之处,有许多尴尬的和过时的条目)namedtuple的噩梦就无法挽回。它导出了巨大的不良功能,这对维护代码将是一个噩梦,它也仅仅能解决不到一半的问题。去一一列举它的缺点是无聊的,不过却有几个主要的缺点:
1.它的field被大量的外部索引所调用,不管你想不想,这意味这你没有一个私有的属性,因为所有的属性都可以被 __getitem__ 从外部获取。
2.它同元组有着相同的价值,所以很容易陷入类型混乱之中,特别是当你向从元组和列表中迁移出来的时候。
3.它是个元组,所以它的值是不可变的。
对于最后一点,你可以这样尝试:
在某种意义下它不像一个类型;简单的不带特殊情况的语法分析工具不能辨别出它。你不能给它其他的方法,因为没有地方去放置。更不要提你需要输入类名两次这个问题了。
你可以通过继承这样做:
这会给你一个放置方法的地方,通常它看起来像一个类,然而...但是反过来,你得到一个奇怪的内部名称(顺便说一句,是显示在 repr 中的名字,而不是类的实际名字)。然后,你也默默的创建了此处未列出的可变属性,这是添加类声明的副作用。除非你把 __slots__ = 'x y z'.split() 加入类里面,不过这样我们也仅仅是通过打印属性名两次来完成。
这甚至没有提到,科学表明你不应该使用继承。
所以,如果你完全掌握它, namedtuple 可以作为一个提升的方法,但是仅仅在一些方面,它也有自己的类库。
进入 attr
这是我最喜欢的Python类库了。
让我们检查一下上面的问题。我该怎样用 attr 来处理 Point3D 呢?
因为它不是内置在语言中,所以我们需要两行代码来开始。导入和装饰器表明我们即将开始使用它。
看,没有继承!通过一个装置器,Point3D 保持了一个原版的类。
它有一个叫做 x 的属性。
还有一个 y ,一个 z 。我们已经完成了。
我们完成了吗?等一下,用一个字符串表示怎么样?
比较?
Ok,如果我们想要获得JSON序列化标准的数据应该怎么做?
也许这只是一个小小的功能。但是不管怎么说,这是因为使用了 attr 而变的简单的许多事的一个,因为 attrs 允许你在你的类中声明field和许多关于他们的元数据,然后返回元数据。
我并没有深入的介绍一些关于 attrs 有趣的特点;你可以通过官方文档了解更多。另外,它是完美维护的,所以经常会出现一些令人惊喜的新功能。但是 attrs 可以做许多核心功能。如果你使用了它,你会明白你以前Python白学了:
1.它允许你定义简洁的类型,而不是那些冗长的 def __init__ ... 。类型无需键
入。
2.它可以让你直接表达自己的意思,而不是拐弯抹角的表达它。
3.它提供了有用的默认值,而不是那些有时有用有时无用的原装默认值。
4.它给了你可扩展的空间,让事情更简单。
增强版
因为我不是要讲到它的每一个特点,所以我也会有所遗漏。你可以看看上面 repr() 代码包含的长长的属性名。这也许很有趣。
如果你想处理一些坐标为浮点数的值:
当我们使用attrs 时我们可以添加额外的参数,如果需要的话,我们可以为每一个属性添加说明。这些特性让我们避免了一些常见的问题。比如下面的著名的'找错误'程序:
修改后,它是这样的:
加了两行代码而已。
contents 成为了全局变量,它可以使没有传入参数的 blog 对象得到一个默认参数。如果使用 attrs 就会变成这样:
attrs 提供了许多方法使你的类即方便又整洁。
Python的未来
许多人因为可以使用Python3而很兴奋,我所希望的却是可以看到 attrs 可以广泛的应用于任何地方。它是我见过的代码库中最有效的类库。
试试看,你会惊奇的发现你可以使用一个整齐的类,而以前你可能使用的是并不太友好的元组,列表或者一个字典。你可能会发现你会爱上它的,并且不断的使用它,因为我就是这样。
英文原文:https://glyph.twistedmatrix.com/2016/08/attrs.html?utm_source=twitterfeed&utm_medium=twitter
译者:李旭