|
| 1 | +# collection小技巧 |
| 2 | +## 1.序列连接操作符 |
| 3 | +连接操作符( + )这个操作符允许我们把一个序列和另一个相同类型的序列做连接。 |
| 4 | + |
| 5 | + sequence1 + sequence2 |
| 6 | +对字符串来说,这个操作不如把所有的子字符串放到一个列表或可迭代对象中,然后调用一个join方法来把所有的内容连接在一起节约内存;对列表来说,推荐用列表类型的extend()方法来把两个或者多个列表对象合并. |
| 7 | + |
| 8 | +## 2.序列切片 |
| 9 | + |
| 10 | + >>> s = 'abcdefgh' |
| 11 | + >>> s[::-1] # 可以视作"翻转"操作 |
| 12 | + 'hgfedcba' |
| 13 | + >>> s[::2] # 隔一个取一个的操作 |
| 14 | + 'aceg |
| 15 | + |
| 16 | +切片索引的开始和结束素引值可以超过字符串的长度。换句话说,起始索引可以小于0,而对于结束索引,即使索引值为100 的元素并不存在也不会报错. |
| 17 | + |
| 18 | + >>> ('Faye', 'Leanna', 'Daylen')[-100:100] |
| 19 | + ('Faye', 'Leanna', 'Daylen') |
| 20 | + |
| 21 | +有这么一个问题:有一个字符串,我们想通过一个循环按照这样的形式显示它:每次都把位于最后的一个字符砍掉,下面是实现这个要求的一种方法: |
| 22 | + |
| 23 | + >>> s = 'abcde' |
| 24 | + >>> i = -1 |
| 25 | + >>> for i in range(-1, -len(s), -1): |
| 26 | + ... print s[:i] |
| 27 | + ... |
| 28 | + abcd |
| 29 | + abc |
| 30 | + ab |
| 31 | + a |
| 32 | + |
| 33 | +因为-1 已经是“最小”的索引了.我们不可能用0 来作为索引值,因为这会切片到第一个元素之前而什么都不会显示: |
| 34 | + |
| 35 | + >>> s[:0] |
| 36 | + '' |
| 37 | + |
| 38 | +我们的方案是使用另一个小技巧:用None 作为索引值 |
| 39 | + |
| 40 | + >>> s = 'abcde' |
| 41 | + >>> for i in [None] + range(-1, -len(s), -1): |
| 42 | + ... print s[:i] |
| 43 | + ... |
| 44 | + abcde |
| 45 | + abcd |
| 46 | + abc |
| 47 | + ab |
| 48 | + a |
| 49 | + |
| 50 | +似乎还可以先创建一个只包含None 的列表,然后用extend()函数把range()的输出添加到这个列表,或者先建立range()输出组成的列表然后再把None 插入到这个列表的最前面,然后对这个列表进行遍历: |
| 51 | + |
| 52 | + >>> for i in [None].extend(range(-1, -len(s), -1)): |
| 53 | + ... print s[:i] |
| 54 | + ... |
| 55 | + Traceback (most recent call last): |
| 56 | + File "<stdin>", line 1, in ? |
| 57 | + TypeError: iteration over non-sequence |
| 58 | + |
| 59 | +这个错误发生的原因是[None].extend(...)函数返回None , None 既不是序列类型也不是可迭代对象. |
| 60 | + |
| 61 | +## 3.序列的可变对象与不可变对象 |
| 62 | +字符串是一种不可变数据类型。就是说它的值是不能被改变或修改的。这就意味着如果你想修改一个字符串,或者截取一个子串,或者在字符串的末尾连接另一个字符串等等,你必须新建一个字符串。 |
| 63 | + |
| 64 | + >> s = 'abc' |
| 65 | + >>> |
| 66 | + >>> id(s) |
| 67 | + 135060856 |
| 68 | + >>> |
| 69 | + >>> s += 'def' |
| 70 | + >>> id(s) |
| 71 | + 135057968 |
| 72 | + |
| 73 | +对字符串的一个字符或者一片字符的改动都是不被允许的: |
| 74 | + |
| 75 | + >>> s |
| 76 | + 'abcdef' |
| 77 | + >>> |
| 78 | + >>> s[2] = 'C' |
| 79 | + Traceback (innermost last): |
| 80 | + File "<stdin>", line 1, in ? AttributeError: __setitem__ |
| 81 | + >>> |
| 82 | + >>> s[3:6] = 'DEF' |
| 83 | + Traceback (innermost last): |
| 84 | + File "<stdin>", line 1, in ? |
| 85 | + AttributeError: __setslice__ |
| 86 | + |
| 87 | +那些可以改变对象值的可变对象的方法是没有返回值的: |
| 88 | + |
| 89 | + >>> music_media.sort()# 没有输出? |
| 90 | + >>> |
| 91 | + |
| 92 | +在使用可变对象的方法如sort(),extend()和reverse()的时候要注意,这些操作会在列表中原地执行操作,也就是说现有的列表内容会被改变,但是没有返回值!是的,与之相反,字符串方法确实有返回值: |
| 93 | + |
| 94 | + >>> 'leanna, silly girl!'.upper() |
| 95 | + 'LEANNA, SILLY GIRL!' |
| 96 | + |
| 97 | +虽然元组对象本身是不可变的,但这并不意味着元组包含的可变对象也不可变了: |
| 98 | + |
| 99 | + >>> t = (['xyz', 123], 23, -103.4) |
| 100 | + >>> t |
| 101 | + (['xyz', 123], 23, -103.4) |
| 102 | + >>> t[0][1] |
| 103 | + 123 |
| 104 | + >>> t[0][1] = ['abc', 'def'] |
| 105 | + >>> t |
| 106 | + (['xyz', ['abc', 'def']], 23, -103.4) |
| 107 | + |
| 108 | +## 4.序列类型对象的拷贝 |
| 109 | +序列类型对象的浅拷贝是默认类型拷贝,并可以以下几种方式实施:(1)完全切片操作[:],(2)利用工厂函数,比如list(),dict()等,(3)使用copy 模块的copy 函数. |
| 110 | + |
| 111 | + >>> person = ['name', ['savings', 100.00]] |
| 112 | + >>> hubby = person[:] # slice copy |
| 113 | + >>> wifey = list(person) # fac func copy |
| 114 | + >>> [id(x) for x in person, hubby, wifey] |
| 115 | + [11826320, 12223552, 11850936] |
| 116 | + |
| 117 | +当进行列表复制时,第一个对象是不可变的(是个字符串类型),而第二个是可变的(一个列表).正因为如此,当进行浅拷贝时,字符串被显式的拷贝,并新创建了一个字符串对象,而列表元素只是把它的引用复制了一下。 |
| 118 | + |
| 119 | + >>> hubby[0] = 'joe' |
| 120 | + >>> wifey[0] = 'jane' |
| 121 | + >>> hubby, wifey |
| 122 | + (['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]]) |
| 123 | + >>> hubby[1][1] = 50.00 |
| 124 | + >>> hubby, wifey |
| 125 | + (['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]]) |
| 126 | + |
| 127 | +要得到一个完全拷贝或者说深拷贝--创建一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用--需要copy.deepcopy()函数。 |
| 128 | + |
| 129 | + >>> person = ['name', ['savings', 100.00]] |
| 130 | + >>> hubby = person |
| 131 | + >>> import copy |
| 132 | + >>> wifey = copy.deepcopy(person) |
| 133 | + >>> [id(x) for x in person, hubby, wifey] |
| 134 | + [12242056, 12242056, 12224232] |
| 135 | + >>> hubby[0] = 'joe' |
| 136 | + >>> wifey[0] = 'jane' |
| 137 | + >>> hubby, wifey |
| 138 | + (['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]]) |
| 139 | + >>> hubby[1][1] = 50.00 |
| 140 | + >>> hubby, wifey |
| 141 | + (['joe', ['savings', 50.0]], ['jane', ['savings', 100.0]]) |
| 142 | + |
| 143 | +非容器类型(比如数字,字符串和其他"原子"类型的对象,像代码,类型和xrange 对象等)没有被拷贝一说。 |
| 144 | + |
| 145 | +如果元组变量只包含原子类型对象,对它的深拷贝将不会进行。 |
| 146 | + |
| 147 | + >>> person = ['name', ('savings', 100.00)] |
| 148 | + >>> newPerson = copy.deepcopy(person) |
| 149 | + >>> [id(x) for x in person, newPerson] |
| 150 | + [12225352, 12226112] |
| 151 | + >>> [id(x) for x in person] |
| 152 | + [9919616, 11800088] |
| 153 | + >>> [id(x) for x in newPerson] |
| 154 | + [9919616, 11800088] |
| 155 | + |
| 156 | +## 5.访问字典中的值 |
| 157 | +如果我们想访问该字典中的一个数据元素,而它在这个字典中没有对应的键,将会产生一个错误: |
| 158 | + |
| 159 | + >>> dict2['server'] Traceback (innermost last): |
| 160 | + File "<stdin>", line 1, in ? |
| 161 | + KeyError: server |
| 162 | + |
| 163 | +检查一个字典中是否有某个键的方法使用in 或 not in 操作符: |
| 164 | + |
| 165 | + >>> 'server' in dict |
| 166 | + False |
| 167 | + >>> 'name' in dict |
| 168 | + True |
| 169 | + |
| 170 | +## 6.字典排序 |
| 171 | +```python |
| 172 | +mydict = {'carl':40, 'alan':2, 'bob':1, 'danny':3} |
| 173 | +``` |
| 174 | + |
| 175 | +基于key排序: |
| 176 | +```python |
| 177 | +for key in sorted(mydict.iterkeys()): |
| 178 | + print "%s: %s" % (key, mydict[key]) |
| 179 | +``` |
| 180 | + |
| 181 | +Results: |
| 182 | + |
| 183 | + alan: 2 |
| 184 | + bob: 1 |
| 185 | + carl: 40 |
| 186 | + danny: 3 |
| 187 | + |
| 188 | +基于value排序: |
| 189 | +```python |
| 190 | +for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)): |
| 191 | + print "%s: %s" % (key, value) |
| 192 | +``` |
| 193 | + |
| 194 | +Results: |
| 195 | + |
| 196 | + bob: 1 |
| 197 | + alan: 2 |
| 198 | + danny: 3 |
| 199 | + carl: 40 |
| 200 | + |
| 201 | +取出最大和最小key: |
| 202 | +```python |
| 203 | +min_key = min(mydict.keys()) |
| 204 | +max_key = max(mydict.keys()) |
| 205 | +``` |
| 206 | + |
| 207 | +使用iterator遍历,dict有几种迭代子,它们分别是:iteritems, iterkeys, itervalues。下面就iteritems给出一个使用的例子: |
| 208 | +```python |
| 209 | +for k,v in myDict.iteritems(): |
| 210 | + print k,v |
| 211 | +``` |
| 212 | + |
| 213 | +## 7.判断一个 list 是否为空 |
| 214 | +传统的方式: |
| 215 | +```python |
| 216 | +if len(mylist): |
| 217 | + # Do something with my list |
| 218 | +else: |
| 219 | + # The list is empty |
| 220 | +``` |
| 221 | + |
| 222 | +由于一个空 list 本身等同于 False,所以可以直接: |
| 223 | +```python |
| 224 | +if mylist: |
| 225 | + # Do something with my list |
| 226 | +else: |
| 227 | + # The list is empty |
| 228 | +``` |
| 229 | + |
| 230 | +## 8.遍历 list 的同时获取索引 |
| 231 | +传统的方式: |
| 232 | +```python |
| 233 | +i = 0 |
| 234 | +for element in mylist: |
| 235 | + # Do something with i and element |
| 236 | + i += 1 |
| 237 | +``` |
| 238 | + |
| 239 | +这样更简洁些: |
| 240 | +```python |
| 241 | +for i, element in enumerate(mylist): |
| 242 | + # Do something with i and element |
| 243 | + pass |
| 244 | +``` |
| 245 | + |
| 246 | +## 9.list 排序 |
| 247 | +在包含某元素的列表中依据某个属性排序是一个很常见的操作。例如这里我们先创建一个包含 person的list: |
| 248 | +```python |
| 249 | +class Person(object): |
| 250 | + def __init__(self, age): |
| 251 | + self.age = age |
| 252 | + |
| 253 | +persons = [Person(age) for age in (14, 78, 42)] |
| 254 | +``` |
| 255 | + |
| 256 | +传统的方式是: |
| 257 | +```python |
| 258 | +def get_sort_key(element): |
| 259 | + return element.age |
| 260 | + |
| 261 | +for element in sorted(persons, key=get_sort_key): |
| 262 | + print "Age:", element.age |
| 263 | +``` |
| 264 | + |
| 265 | +更加简洁、可读性更好的方法是使用 Python 标准库中的 operator 模块。attrgetter 方法优先返回读取的属性值作为参数传递给 sorted 方法。operator 模块还包括 itemgetter 和 methodcaller 方法,作用如其字面含义。 |
| 266 | +```python |
| 267 | +from operator import attrgetter |
| 268 | + |
| 269 | +for element in sorted(persons, key=attrgetter('age')): |
| 270 | + print "Age:", element.age |
| 271 | +``` |
| 272 | + |
| 273 | +## 10.在 Dictionary 中元素分组 |
| 274 | +```python |
| 275 | +class Person(object): |
| 276 | + def __init__(self, age): |
| 277 | + self.age = ageperson |
| 278 | + |
| 279 | +s = [Person(age) for age in (78, 14, 78, 42, 14)] |
| 280 | +``` |
| 281 | + |
| 282 | +如果现在我们要按照年龄分组的话,一种方法是使用 in 操作符: |
| 283 | +```python |
| 284 | +persons_by_age = {} |
| 285 | + |
| 286 | +for person in persons: |
| 287 | + age = person.age |
| 288 | + if age in persons_by_age: |
| 289 | + persons_by_age[age].append(person) |
| 290 | + else: |
| 291 | + persons_by_age[age] = [person] |
| 292 | + |
| 293 | +assert len(persons_by_age[78]) == 2 |
| 294 | +``` |
| 295 | + |
| 296 | +相比较之下,使用 collections 模块中 defaultdict 方法的途径可读性更好。defaultdict 将会利用接受的参数为每个不存在的 key 创建对应的值,这里我们传递的是 list,所以它将为每个 key 创建一个 list 类型的值: |
| 297 | +```python |
| 298 | +from collections import defaultdict |
| 299 | + |
| 300 | +persons_by_age = defaultdict(list) |
| 301 | +for person in persons: |
| 302 | + persons_by_age[person.age].append(person) |
| 303 | +``` |
0 commit comments