Skip to content

Commit d68be65

Browse files
authored
Create collection.md
1 parent b5ba60d commit d68be65

1 file changed

Lines changed: 303 additions & 0 deletions

File tree

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
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

Comments
 (0)