网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 
中国网管联盟
Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道
 当前位置: > bitsCN.com > linux > Linux编程 > Python编程 > Python 之优雅与瑕疵  

Python 之优雅与瑕疵

2007-05-27  作者:bitsCN整理  来源:中国网管联盟  点评 投稿 收藏

自从 Python 1.5.2(一个长期以来一直稳定且可靠的版本)迈入 “黄金时代” 以来,Python 增加了许多语法特性以及内置函数和类型。这些改进单独地看都是合理的调整,但是作为一个整体,它们使 Python 变得更加复杂,不再是有经验的程序员 “花上一个下午” 就能够掌握的语言了;另外,一些修改在带来好处的同时也有缺陷。

    在本文中,我要讨论在最近几个 Python 版本中增加的不那么引人注目的特性,我将分析哪些改进具有真正的价值,哪些特性只是不必要地增加了复杂性。我希望向所有并非一直使用 Python 的程序员指出真正具有价值的东西。这包括使用其他语言的程序员以及只将编程当做副业的科学家。当遇到一些难题时,我会提供解决方案。

网管论坛bbs_bitsCN_com

不可比较的麻烦

网管u家u.bitsCN.com

    在 Python 2.0 和 Python 2.1 之间,发生了一些奇怪的变化。以前可以比较的对象在进行比较时却引发了异常。具体地说,复数无法与其他数字进行比较了,包括其他复数以及整数、浮点数和长整数。实际上,在此之前,比较 Unicode 字符串和文本字符串时就可能会遇到这个问题,但那只发生在一些极端情况下。

网管网www.bitscn.com

    我认为,这些修改很怪异,没有必要。在 1.5.2 的黄金时代,无论比较什么对象,不等操作符总会返回一个结果。当然,结果不一定是有意义的 —— 比如字符串和浮点数的比较就没有意义。但是,至少我们总会得到一个一致的结果。

网管论坛bbs_bitsCN_com

    出现这些修改之后,一些 Python 支持者认为不允许对不同类型的对象进行不等比较是件好事,只有定义了定制的比较函数之后,才能进行这种比较。我觉得,在处理定制类和多重继承时,编写定制的比较函数实际上很需要技巧。另外,不能在浮点数、整数和长整数(比如 decimal)之间进行比较是非常不方便的。但是,或许可以定义一个合理的规则。

网管联盟bitsCN@com

    但是,无论定义什么样的规则,它都与 Python 过去的做法有非常大的差异。现在的情况是比较行为无规律可循,即使知道比较的对象的类型,也无法确定它们是否是可比较的(而且不等性既非可传递也非封闭式):

网管论坛bbs_bitsCN_com


清单 1. 比较是否成功取决于类型和值
>>> map(type, (u1, s1, s2))
[<type 'unicode'>, <type 'str'>, <type 'str'>]
>>> u1 < s1
True
>>> s1 < s2
True
>>> u1 < s2
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf0 in position 0:
    ordinal not in range(128)
>>> map(type, (n, j, u1))
[<type 'int'>, <type 'complex'>, <type 'unicode'>]
>>> n < u1
True
>>> j < u1
True
>>> n < j
      TypeError: no ordering relation is defined for complex numbers 网管bitscn_com

更糟糕的是,复数现在不能与大多数 数字值进行比较,但是可以通过大多数非数字值判断出绝对的不等性。例如,考虑到理论纯洁性,我知道 1+1j 与 2-3j 的比较是没有意义的,但是为什么有下面这样的结果:

中国网管联盟bitsCN.com


清单 2. 令人吃惊的比较结果
>>> 2-3j < 'spam'
True
>>> 4+0j < decimal.Decimal('3.14')
True
>>> 4+0j < 5+0j
TypeError: no ordering relation is defined for complex numbers 网管bitscn_com


从理论上来讲,这全无 “纯” 可言。 网管论坛bbs_bitsCN_com

一个真正的瑕疵:对异构集合进行排序

中国网管论坛bbs.bitsCN.com

    自变量有时候会造成编程错误,试图对不可比较的类型进行比较。但是 Python 可以顺利地执行许多这种类型的比较;并且依照 “duck typing” 哲学来完成这样的任务(duck typing 是指 “如果看起来像鸭子,听起来像鸭子,就可以把它当作鸭子”,也就是说,不管对象是 什么,只在乎它做 什么。)Python 集合常常将不同类型的对象组织在一起,希望能够做 与其中的各对象相似的事情。一种常见的用例是对一组不同类型的值进行编码,以便通过某种协议进行传输。

网管bitscn_com

    对于这其中的大多数值,不等比较是不必要的。但是,在一种常见的情况下,不等性是非常有用的;那就是对集合进行排序 时,通常是对列表或与列表类似的定制集合进行排序。有时候,需要以一种有意义的升序来处理集合(例如,按照数据值从小到大的次序)。在其他时候,需要为多个集合创建一种固定的次序,尤其是为了对两个集合执行某种类似于 “list diff” 的处理时。也就是说,如果一个对象在这两个集合中都存在,那么就执行一种操作;如果它只在一个集合中存在,就执行另一种操作。不断地检查 if x in otherlist 会导致效率成 big-O 式几何级数递减;在两个固定排序的列表之间进行平行匹配的效率要高得多。例如:

网管bitscn_com


清单 3. 根据两个列表的成员关系执行不同的操作
list1.sort()
list2.sort()
list2_xtra = []
list2_ndx = 0
for it1 in list1:
    it2 = list2[list2_ndx]
    while it1 < it2:
        list2_ndx += 1
        it2 = list2[list2_ndx]
        if it1 == it2:
            item_in_both(it1)
        elif it1 > it2:
            item_in_list1(it1)
        else:
            list2_xtra.appen(it2)
 for it2 in list2_xtra:
           item_in_list2(it2) 网管bitscn_com

有时候,有意义比较的 “局部序列” 是有用的,甚至在出现不同类型对象的情况下也是如此(例如,“依次” 处理所有浮点值,即使它们与其他地方处理的字符串没有可比性)。 网管联盟bitsCN_com

排序失败

网管联盟bitsCN_com

    当然,上面执行 “list diff” 的代码几乎可以任意扩展。例如,list1 和 list2 可以是下面这样的小列表的集合。请试着猜一下哪些部分是可以排序的: 网管网www_bitscn_com


清单 4. 可排序和不可排序列表的大杂烩
['x','y','z', 1],
['x','y','z', 1j],
['x','y','z', 1j, 1],       # Adding an element makes it unsortable
[0j, 1j, 2j],               # An obvious "natural" order
[0j, 1, 2],
[0, 1, 2],                  # Notice that 0==0j --> True
[chr(120), chr(240)],
[chr(120), chr(240), 'x'],
[chr(120), chr(240), u'x'], # Notice u'x'=='x' --> True
[u'a', 'b', chr(240)],
[chr(240), u'a', 'b']       # Same items, different initial order

网管bitscn_com


我编写了一个小程序来尝试排序各列表: 网管u家u.bitsCN.com


清单 5. 对各列表进行排序的结果
% python compare.py
(0)  ['x', 'y', 'z', 1] --> [1, 'x', 'y', 'z']
(1)  ['x', 'y', 'z', 1j] --> [1j, 'x', 'y', 'z']
(2)  ['x', 'y', 'z', 1j, 1] --> exceptions.TypeError
(3)  [0j, 1j, 2j] --> exceptions.TypeError
(4)  [0j, 1, 2] --> exceptions.TypeError
(5)  [0, 1, 2] --> [0, 1, 2]
(6)  ['x', '\xf0'] --> ['x', '\xf0']
(7)  ['x', '\xf0', 'x'] --> ['x', 'x', '\xf0']
(8)  ['x', '\xf0', u'x'] --> exceptions.UnicodeDecodeError
(9)  [u'a', 'b', '\xf0'] --> [u'a', 'b', '\xf0']
(10) ['\xf0', u'a', 'b'] --> exceptions.UnicodeDecodeError 网管下载dl.bitscn.com


    通过前面的解释,或多或少能够猜出一部分结果。但是,看一下 (9) 和 (10),这两个列表以不同次序包含完全相同的对象:由此可见,排序是否失败不但取决于列表中 对象的类型和值,还取决于 list.sort() 方法的特定实现! 网管u家u.bitsCN.com

修订比较

网管u家u.bitscn@com

    自 1.5.2 以来,Python 发展出了一种非常有用的数据类型:集(set),它最初是一个标准模块,后来成了一种内置类型(模块还包含一些额外的特性)。对于上面描述的许多问题,只需使用集来取代列表即可轻松地判断对象是在一个集合中、在另一个集合中还是同时存在于两个集合中,而不需要编写自己的 “list diff” 代码。例如: 网管论坛bbs_bitsCN_com


清单 6. 集与集操作
>>> set1 = set([1j, u'2', 3, 4.0])
>>> set2 = set([4, 3, 2, 1])
>>> set1 | set2
set([3, 1, 2, 1j, 4.0, u'2'])
>>> set1 & set2
      set([3, 4]) 网管联盟bitsCN@com


TAGs优雅   比较   对象   Python   一个   序列   类型   进行   可以   排序    
 上一篇:没有了   下一篇:没有了
Python 之优雅与瑕疵 评论:
loading.. 评论加载中…
评论:请自觉遵守互联网相关政策法规,评论不得超过250字。

验证码: 注册用户
本类热门排行:
最新推荐文章:
网管论坛交流: