阿布云

你所需要的,不仅仅是一个好用的代理。

Python 列表理解

阿布云 发表于

2.png

示例运行环境: CPython 3.6.1, macOS 10.12.5

鉴于不同运行环境差异,示例输出结果会有所不同。尤其是 id,以及内存地址等信息。  请以实际运行结果为准。

列表

仅从操作方式上看,列表 (list) 像是数组和链表的综合体。除按索引访问外,还支持插入、追加、删除等操作,完全可视作队列 (queue) 或栈 (stack)结构使用。如不考虑性能问题,似乎是一种易用且功能完善的理想数据结构。

>> > x = [ 1 , 2 ]

>> > x [ 1 ]

2

>> > x . insert ( 0 , 0 )

>> > x

[ 0 , 1 , 2 ]

>> > x . reverse ( )

>> > x

[ 2 , 1 , 0

]

queue

>> > q = [ ]

>> > q . append ( 1 ) # 向队列追加数据。

>> > q . append ( 2 )

>> > q . pop ( 0 ) # 按追加顺序弹出数据。

1

>> > q . pop ( 0 )

2

对于有大量写操作的专职队列或栈,建议使用 collections.deque、queue 等类型。

列表内部结构由两部分组成,头部保存元素和内存分配计数,另引用独立指针数组。所有列表项(item) 通过该数组保存指针引用,并不会嵌入元素实际内容。

1.png

作为使用频率最高的数据结构之一,列表的性能优化很重要。那么,固定长度的头部结构, 很容易实现内存复用。创建时,优先从复用区获取。而当列表被回收,除非超出最大复用数量限制(默认 80),否则会被放回复用区,而不是交还内存。每次真正需要分配和释放内存的是指针数组。

用数组而非链表存储元素项引用,也有实际考虑。从读操作看,无论遍历还是基于序号访问,数组性能总是最高。尽管插入、删除等变更操作需移动内存,但也仅仅是指针复制,无关元素大小,不会有太高消耗。如果列表太大,或写操作远多于读,那么应当使用针对性的数据结构,而非通用设计的内置列表类型。

另外,指针数组内存分配算法基于元素数量和剩余空间大小,按相应比率进行扩容或收缩, 而非逐项进行。如此,可避免太频繁的内存分配操作。

构建

用方括号指定显式元素的构建语法最为常用。当然,也可基于类型创建实例,接收一个可迭代对象作为初始内容。不同于数组,列表仅存储指针,对元素类型并不关心,故可以是不同类型混合。

>> > [ 1 , "abc" , 3.14 ]

[ 1 , 'abc' , 3.14 ]

>> > list ( "abc" ) # iterable

[ 'a' , 'b' , 'c'

]

另有一种称做推导式 (comprehension) 的语法。同样用方括号,但以 for 循环初始化元素, 并可选 if 表达式作为过滤条件。

>> > [ x + 1 for x in range ( 3 ) ]

[ 1 , 2 , 3 ]

>> > [ x for x in range ( 6 ) if x % 2 == 0 ]

[ 0 , 2 , 4

]

其行为类似以下代码。

>> > d = [ ]

>> > for x in range ( 6 ) :

if x % 2 == 0 :

d . append ( x )

>> > d

[ 0 , 2 , 4

]

有种称做 Pythonic 的习惯,核心是写出简洁的代码,推导式算其中一种。 有关推导式更多信息,可阅读后章。

无论是历史原因,还是实现方式,内置类型关注性能要多过设计。如要实现自定义列表,建议基于 collections.UserList 包装类型完成。除统一 collections.abc 体系外,最重要的是该类型重载并完善了相关操作符方法。

>> > list . __bases__

( object , )

>> > collections . UserList . __bases__

( collections . abc . MutableSequence ,

)

以加法操作符为例,对比不同继承的结果。

>> > class A ( list ) : pass

>> > type ( A ( "abc" ) + list ( "de" ) ) # 返回的是 list, 而不是 A。

list

>> > class B ( collections . UserList ) : pass

>> > type ( B ( "abc" ) + list ( "de" ) ) # 返回 B 类型。

__main__

.

B

最小接又设计是个基本原则。应慎重考虑列表这种功能丰富的类型,是否适合作为基类。

操作

用加法运算符连接多个列表,乘法复制内容。

>> > [ 1 , 2 ] + [ 3 , 4 ]

[ 1 , 2 , 3 , 4 ]

>> > [ 1 , 2 ] * 2

[ 1 , 2 , 1 , 2

]

注意,同时加法(或乘法)运算,但结果却有所不同。

>> > a = [ 1 , 2 ]

>> > b = a

>> > a = a + [ 3 , 4 ]

 

# 新建列表对象,然后与 a 关联。

>> > a   # a、b 结果不同,确定 a 指向新对象。

[ 1 , 2 , 3 , 4 ]

>> > b

[ 1 , 2

]

>> > a = [ 1 , 2 ]

>> > b = a

>> > a += [ 3 , 4 ] # 直接修改 a 内容。

>> > a   # a、b 结果相同,确认修改原对象。

[ 1 , 2 , 3 , 4 ]

>> > b

[ 1 , 2 , 3 , 4 ]

>> > a is b

True

编译器将 “+=” 运算符处理成 INPLACE_ADD 操作,也就是修改原数据,而非新建对象。其效 果类似于执行 list.extend 方法。

判断元素是否存在时,同样习惯使用 in,而非 index 方法。

>> > 2 in [ 1 , 2 ]

True

至于删除操作,可以索引序号指定单个元素,或用切片指定删除范围。

>> > a = [ 0 , 1 , 2 , 3 , 4 , 5 ]

>> > del a [ 5 ] # 删除单个元素。

>> > a

[ 0 , 1 , 2 , 3 , 4 ]

>> > del a [ 1 : 3 ] # 删除范围。

>> > a

[ 0 , 3 , 4

]

返回切片时会创建新列表对象,并复制相关指针数据到新的数组。除部分引用目标相同外,对列表自身的修改(插入、删除等)互不影响。

>> > a = [ 0 , 2 , 4 , 6 ]

>> > b = a [ : 2 ]

>> > a [ 0 ] is b [ 0 ] # 复制引用,指向同一对象。

True

>> > a . insert ( 1 , 1 ) # 对 a  表的操作,不会影响 b。

>> > a

[ 0 , 1 , 2 , 4 , 6 ]

>> > b

[ 0 , 2

]

复制的是指针 (引用),而不是目标元素对象。 对列表自身的修改互不影响,但对目标元素的修改是共享的。

2.png

对列表排序可设定自定义条件,比如按字段或长度等。

class User :

def __init__ ( self , name , age ) :

self . name = name

self . age = age

 

def __repr__ ( self ) :

return

f

"{self.name} {self.age}"

>> > users = [ User ( f "user{i}" , i ) for i in ( 3 , 1 , 0 , 2 ) ]

>> > users

[ user3 3 , user1 1 , user0 0 , user2 2

]

>> > users . sort ( key = lambda u : u . age ) # 使 lambda 匿名函数返回排序条件。

>> > users

[ user0 0 , user1 1 , user2 2 , user3 3

]

如要返回排序复制品,可使用 sorted 函数。

>> > d = [ 3 , 0 , 2 , 1 ]

>> > sorted ( d ) # 同样可指定排序条件,或倒序。

[ 0 , 1 , 2 , 3 ]

>> > d   # 并未影响原列表。

[ 3 , 0 , 2 , 1

]

向有序列表插入元素,可借助 bisect 模块。它使用二分法查找适合位置,可用来实现优先级队列或一致性哈希算法等。

>> > d = [ 0 , 2 , 4 ]

>> > import bisect

>> > bisect . insort_left ( d , 1 ) # 插入新元素后,依然保持有序状态。

>> > d

[ 0 , 1 , 2 , 4 ]

>> > bisect . insort_left ( d , 2 )

>> > d

[ 0 , 1 , 2 , 2 , 4 ]

>> > bisect . insort_left ( d , 3 )

>> > d

[ 0 , 1 , 2 , 2 , 3 , 4

]

自定义复合类型,可通过重载比较运算符(__eq__、__lt__ 等)实现自定义排序条件。