第二章(提炼) 序列构成的数组(三)

一. 对序列使用+和*

         Python 程序员会默认序列是支持 + * 操作的。通常 + 号两侧的序列由相同类型的数据所构成,在拼接的过程中,两个被操作的序列都不会被修改,Python 会新建一个包含同样类型数据的序列来作为拼接的结果。

        如果想要把一个序列复制几份然后再拼接起来,更快捷的做法是把这个序列乘以一个整数。+ 和 * 都遵循这个规律,不修改原有的操作对象,而是构建一个全新的序列。

        如果在 a * n 这个语句中,序列 a 里的元素是对其他可变 对象的引用的话,你就需要格外注意了,因为这个式子的结果可能 会出乎意料。比如,你想用 my_list = [[]] * 3 来初始化一个 由列表组成的列表,但是你得到的列表里包含的 3 个元素其实是 3 个引用,而且这 3 个引用指向的都是同一个列表。

演示1  建立由列表组成的列表

上述操作等同于(每次迭代中新建了一个list。作为新的一行添加到board中):

演示2  含有3个指向同一对象的引用的列表是毫无用处的

 

上述错误示范等同于(追加同一个对象3次到board列表):

二. 序列的增量赋值

2.1 +=和*=背后的故事

        增量赋值运算符 += *= 的表现取决于它们的第一个操作对象。+= 背后的特殊方法是 __iadd__ (用于“就地加法”)。但是如果一个类没有实现这个方法的话,Python 会退一步调用 __add__ 。如:在a += b的执行过程中,如果 a 实现了 __iadd__ 方法,就会调用这个方法。同时对可变序列来说,a 会就地改动,就像调用了 a.extend(b) 一样。但是如果 a 没有实现 __iadd__ 的话,a += b 这个表达式的效果就变得跟 a = a + b 一样了:首先计算 a + b,得到一个新的对象,然后赋值给 a。也就是说,在这个表达式中,变量名会不会被关联到新的对象,完全取决于这个类型有没有实现 __iadd__ 这个方法。

        总体来讲,可变序列一般都实现了 __iadd__ 方法,因此 += 是就地加 法。而不可变序列根本就不支持这个操作,对这个方法的实现也就无从 谈起。 上面所说的这些关于 += 的概念也适用于 *=,不同的是,后者相对应的 是 __imul__

演示3  *=在可变和不可变序列上的对比

        对不可变序列进行重复拼接操作的话,效率会很低,因为每次都有一个新对象,而解释器需要把原来对象中的元素先复制到新的对象里,然后再追加新的元素。

        str 是一个例外,因为对字符串做 += 实在是太普遍了,所以 CPython 对它做了优化。为 str 初始化内存的时候,程序会为它留出额外的可扩展空间,因此进行增量操作的时候,并不会涉及复制原有字符串到新位置这类操作。

2.2 一个关于+=的谜题

        上述代码中,因为 tuple 不支持对它的元素赋值,所以会抛出 TypeError 异常。但与此同时元组却被改变了:t= (1, 2, [30, 40, 50, 60])。这是非常罕见的边界情况,因此我们需要注意以下3点:

  • 不要把可变对象放在元组里面。
  • 增量赋值不是一个原子操作。我们刚才也看到了,它虽然抛出了异常,但还是完成了操作。

三. list.sort方法和内置函数sorted

        list.sort 方法会就地排序列表,也就是说不会把原列表复制一份。这也是这个方法的返回值是 None 的原因。在这种情况下返回 None 其实是 Python 的一个惯例:如果一个函数或者方法对对象进行的是就地改动,那它就应该返回 None,好让调用者知道传入的参数发生了变动,而且并未产生新的对象。例如,random.shuffle 函数也遵守了这个惯例。(注:用返回 None 来表示就地改动这个惯例有个弊端,那就是调用者无法将其串联起来。

        与 list.sort 相反的是内置函数 sorted,它会新建一个列表作为返回值。这个方法可以接受任何形式的可迭代对象作为参数,甚至包括不可变序列或生成器。而不管 sorted 接受的是怎样的参数,它最后都会返回一个列表。

        不管是 list.sort 方法还是 sorted 函数,都有两个可选的关键字参数:

  • reverse:如果被设定为 True,被排序的序列里的元素会以降序输出;默认值是 False升序
  • key一个只有一个参数的函数,这个函数会被用在序列里的每一个元素上,所产生的结果将是排序算法依赖的对比关键字。这个参数的默认值是恒等函数(identity function),也就是默认用元素自己的值来排序。

演示4  list.sort()和sorted()的用法

        已排序的序列可以用来进行快速搜索,而标准库的 bisect 模块给我们提供了二分查找算法。下一节会详细讲这个函数,顺便还会看看bisect.insort 如何让已排序的序列保持有序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值