我热爱Python,这十多年来它一直是我的主力编程语言。尽管期间也出现过一些有意思的语言(指的是Haskell和Rust),但我还不打算换到其他语言。 这不是说Python本身没有任何问题。在某些情况下,Python会让你更容易犯错,尤其是一些库大量使用类继承,以及God-object反面模式。 导致该情况的一个原因可能是Python是一种非常方便的语言,所以经验欠缺的程序员犯错误后,他们就得继续忍受下去。 但我想,更重要的原因也许是,有时你努力做正确的事,但Python却会因此惩罚你。 在对象设计的大背景下,“正确的事“是指设计体量小并且独立的类,只做一件事,并且把这件事做好。 例如,如果你的对象开始累积大量的私有方法,也许你应该将它们变成私有属性的公有方法。但是,这种事处理起来非常乏味,你可能就不会理会这些。 如果你有一些相关的数据,而且数据之间的关系和行为是需要进行解释的,那么应该定义为对象。 在Python中定义元组和列表非常方便。 刚开始把address=...写成host,port=...可能觉得没什么关系,但很快你就会到处写 [(family,socktype,proto,canonname,sockaddr)]=...这样的语句,这时就该后悔了。 这还是算你走运的情况。如果倒霉的话,你可能得维护 values[0][7][4][HOSTNAME][“canonical”]这样的代码,这时你的心情是痛苦,而不仅仅是后悔了。 这就提出了一个问题:在Python中使用类是否是麻烦?我们来看一个简单的数据结构:一个三维直角坐标。从最简单的开始: 到现在为止还挺好。我们已经有了一个三维点。 接下来呢? 其实,这是有点可惜。我只想对数据的打包,但却不得不覆盖一个Python运行时中的特殊方法,而且命名还是约定俗成的。 但还不算太坏,毕竟所有的编程语言都是按照某种形式组成的怪异符号而已。至少可以看到属性名了,还能说得通。 我已经说过,我想一个x,但现在必须把它指定为一个属性... 绑定到x?呃,很明显... 每个属性都得这么做一次,所以这相当糟糕?每个属性名都得敲3次?!? 好吧。至少定义完了。 什么,难道还没结束吗? 拜托。现在我得每个属性名敲5次了,如果我想在调试时知道属性到底指的是什么的话,如果定义元组的话,就不用这一步了?!?!? 敲 7 次?!?!?!? 敲 9 次?!?!?!?!? 好了,擦汗 - 尽管多了2行代码不是很好,但至少现在我们不用定义其他比较方法了。现在一切搞定了,对吧? 你知道吗? 我受够了。一个类码了 20 行,却还什么事都没做;我们这样做是想解四元方程,而不是定义“可以打印和比较的数据结构”。 我陷入了大量无用的垃圾元组、列表和字典中;用Python定义合适的数据结构是非常麻烦的。 为解决这个难题,标准库给出的解决方案是使用namedtuple 。然而不幸的是初稿(在许多方面与我自己的处理方式有相似的尴尬的和过时之处)namedtuple仍然无法挽救这个现象。 它引入了大量没有必要的公共函数,这对于兼容性维护来说简直就是一场噩梦,并且它连问题的一半都没有解决。这种做法的缺陷太多了,这里只列一些重点:
至于最后一点,你可以像这样使用: 在这种情况下它看起来并不像一种类;无特殊情况下,简单的语法分析工具将不能识别它为类。但是这样你不能给它添加任何其他方法,因为没有地方放任何的方法。更别提你必须输入类的名字两次。 或者你可以使用继承: 尽管这样可以添加方法和文档字符串,看起来也像一个类,但是内部名称(在repr中显示的内容,并不是类的真实名称)变的很怪了。同时,你还不知不觉中把没列出的属性变成了可变的,这是添加class 声明的一个奇怪的副作用;除非你在类主体中添加 __slots__='X Y z'.split(),但这样又回到了每个属性名必须敲两次的情况。 而且,我们还没提科学已经证明不应该使用继承呢。 因此,如果你只能选命名元组,那就选命名元组吧,也算是改进,虽然只是在部分情况下如此。 这时该我最喜欢的Python库出场了。 pip install attrs 我们重新审视一下上述问题。如何使用attrs库编写Point3D? 由于它还没有内置到Python中,所以必须用以上2行开始:导入包然后使用类装饰器。 你看,没有继承!通过使用类装饰器,Point3D仍然是一个普通的Python类(尽管我们一会会看到一些双下划线方法)。 添加属性 x。 再分别添加属性y和z。这样就完成了。 这就OK了?等等。不用定义字符串表示吗? 怎么进行比较? 好的。但如果我想将有明确属性定义的数据提取为适合JSON序列化的格式呢? 也许上边有一点点准确。即使如此,因为使用了attrs后,很多事情都变得更简单了,它允许你在类上声明字段,以及相关的元数据。 我不打算在这里深入介绍attrs的每一个有趣的功能;你可以阅读它的文档。另外,项目会经常更新,每隔一段时间都会有新的东西出现,因此我也可能会漏掉一些重要的功能。但是用上attrs之后 ,你会发现它所做的正是此前Python所缺乏的:
我们详细说明最后一点。 虽然我不打算谈及每一个功能,但如果我没有提到以下几个特点,那我就太不负责任了。你可以从上面这些特别长的Attribute的repr() 中看到一些有趣的东西。 例如:你通过用@attr.s 修饰类来验证属性。比如:Point3D这个类,应该包含数字。为简单起见,我们可以说这些数字为float类型,像这样: 因为我们使用了attrs,这意味着之后有机会进行验证:可以只给每个需要的属性添加类型信息。其中的一些功能,可以让我们避免常见的错误。例如,这是一个很常见的“找 Bug” 面试题: 修正它,正确的代码应该是这个样子: 额外添加了 2 行代码。 这样,contents无意间就成了全局变量,这使得所有没有提供列表的 Bag对象都共享一个列表。使用attrs的话,就变成这样: attrs还提供一些其他的特性,让你在构建类时更方便更正确。另一个很好的例子?如果你严格地管控对象的属性(或在内存使用上更有效率的CPython),你可以在类层级上使用slots=True - 例如@attr.s(slots=True)-自动与attrs声明的__slots__属性匹配。所有这些功能会让通过attr.ib()声明的属性更好更强大。 有人为以后能普遍使用Python3编程而感到高兴。而我期待的是,能够在Python编程时一直用attrs。就我所知,它对每个使用了的代码库都产生了积极、微妙的影响。 试试看:你可能会惊讶地发现,以前用不方便写文档的元组、列表或字典的地方,现在可以使用具备清晰解释的类了。既然编写结构清晰的类型如此简单方便,以后应该会经常使用attrs的。这对你的代码来说是件好事。我就是一个好例子。 作者:linkcheng 文章来源: http://developer.51cto.com/art/201612/525507.htm |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|