天天看點

Python collection小技巧

1.序列連接配接操作符

連接配接操作符( + )這個操作符允許我們把一個序列和另一個相同類型的序列做連接配接。

sequence1 + sequence2
           

對字元串來說,這個操作不如把所有的子字元串放到一個清單或可疊代對象中,然後調用一個join方法來把所有的内容連接配接在一起節約記憶體;對清單來說,推薦用清單類型的extend()方法來把兩個或者多個清單對象合并.

2.序列切片

>>> s = 'abcdefgh'
>>> s[::-1] # 可以視作"翻轉"操作
'hgfedcba'
>>> s[::2] # 隔一個取一個的操作
'aceg
           

切片索引的開始和結束素引值可以超過字元串的長度。換句話說,起始索引可以小于0,而對于結束索引,即使索引值為100 的元素并不存在也不會報錯.

>>> ('Faye', 'Leanna', 'Daylen')[-100:100]
('Faye', 'Leanna', 'Daylen')
           

有這麼一個問題:有一個字元串,我們想通過一個循環按照這樣的形式顯示它:每次都把位于最後的一個字元砍掉,下面是實作這個要求的一種方法:

>>> s = 'abcde'
>>> i = -1
>>> for i in range(-1, -len(s), -1):
...     print s[:i]
...
abcd
abc
ab
a
           

因為-1 已經是“最小”的索引了.我們不可能用0 來作為索引值,因為這會切片到第一個元素之前而什麼都不會顯示:

>>> s[:0]
''
           

我們的方案是使用另一個小技巧:用None 作為索引值

>>> s = 'abcde'
>>> for i in [None] + range(-1, -len(s), -1):
...     print s[:i]
...
abcde
abcd
abc
ab
a
           

似乎還可以先建立一個隻包含None 的清單,然後用extend()函數把range()的輸出添加到這個清單,或者先建立range()輸出組成的清單然後再把None 插入到這個清單的最前面,然後對這個清單進行周遊:

>>> for i in [None].extend(range(-1, -len(s), -1)):
...     print s[:i]
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: iteration over non-sequence
           

這個錯誤發生的原因是[None].extend(…)函數傳回None , None 既不是序列類型也不是可疊代對象.

3.序列的可變對象與不可變對象

字元串是一種不可變資料類型。就是說它的值是不能被改變或修改的。這就意味着如果你想修改一個字元串,或者截取一個子串,或者在字元串的末尾連接配接另一個字元串等等,你必須建立一個字元串。

>> s = 'abc'
>>>
>>> id(s)
135060856
>>>
>>> s += 'def'
>>> id(s)
135057968
           

對字元串的一個字元或者一片字元的改動都是不被允許的:

>>> s
'abcdef'
>>>
>>> s[2] = 'C'
Traceback (innermost last):
File "<stdin>", line 1, in ? AttributeError: __setitem__
>>>
>>> s[3:6] = 'DEF'
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: __setslice__
           

那些可以改變對象值的可變對象的方法是沒有傳回值的:

>>> music_media.sort()# 沒有輸出?
>>>
           

在使用可變對象的方法如sort(),extend()和reverse()的時候要注意,這些操作會在清單中原地執行操作,也就是說現有的清單内容會被改變,但是沒有傳回值!是的,與之相反,字元串方法确實有傳回值:

>>> 'leanna, silly girl!'.upper()
'LEANNA, SILLY GIRL!'
           

雖然元組對象本身是不可變的,但這并不意味着元組包含的可變對象也不可變了:

>>> t = (['xyz', 123], 23, -103.4)
>>> t
(['xyz', 123], 23, -103.4)
>>> t[0][1]
123
>>> t[0][1] = ['abc', 'def']
>>> t
(['xyz', ['abc', 'def']], 23, -103.4)
           

4.序列類型對象的拷貝

序列類型對象的淺拷貝是預設類型拷貝,并可以以下幾種方式實施:(1)完全切片操作[:],(2)利用工廠函數,比如list(),dict()等,(3)使用copy 子產品的copy 函數.

>>> person = ['name', ['savings', 100.00]]
>>> hubby = person[:] # slice copy
>>> wifey = list(person) # fac func copy
>>> [id(x) for x in person, hubby, wifey]
[11826320, 12223552, 11850936]
           

當進行清單複制時,第一個對象是不可變的(是個字元串類型),而第二個是可變的(一個清單).正因為如此,當進行淺拷貝時,字元串被顯式的拷貝,并新建立了一個字元串對象,而清單元素隻是把它的引用複制了一下。

>>> hubby[0] = 'joe'
>>> wifey[0] = 'jane'
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])
           

要得到一個完全拷貝或者說深拷貝–建立一個新的容器對象,包含原有對象元素(引用)全新拷貝的引用–需要copy.deepcopy()函數。

>>> person = ['name', ['savings', 100.00]]
>>> hubby = person
>>> import copy
>>> wifey = copy.deepcopy(person)
>>> [id(x) for x in person, hubby, wifey]
[12242056, 12242056, 12224232]
>>> hubby[0] = 'joe'
>>> wifey[0] = 'jane'
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 100.0]])
           

非容器類型(比如數字,字元串和其他”原子”類型的對象,像代碼,類型和xrange 對象等)沒有被拷貝一說。

如果元組變量隻包含原子類型對象,對它的深拷貝将不會進行。

>>> person = ['name', ('savings', 100.00)]
>>> newPerson = copy.deepcopy(person)
>>> [id(x) for x in person, newPerson]
[12225352, 12226112]
>>> [id(x) for x in person]
[9919616, 11800088]
>>> [id(x) for x in newPerson]
[9919616, 11800088]
           

5.通路字典中的值

如果我們想通路該字典中的一個資料元素,而它在這個字典中沒有對應的鍵,将會産生一個錯誤:

>>> dict2['server'] Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: server
           

檢查一個字典中是否有某個鍵的方法使用in 或 not in 操作符:

>>> 'server' in dict
False
>>> 'name' in dict
True
           

6.字典排序

mydict = {'carl':, 'alan':, 'bob':, 'danny':}
           

基于key排序:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])
           

Results:

alan: 2
bob: 1
carl: 40
danny: 3
           

基于value排序:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)
           

Results:

bob: 1
alan: 2
danny: 3
carl: 40
           

取出最大和最小key:

min_key = min(mydict.keys())
max_key = max(mydict.keys())
           

使用iterator周遊,dict有幾種疊代子,它們分别是:iteritems, iterkeys, itervalues。下面就iteritems給出一個使用的例子:

for k,v in myDict.iteritems():
    print k,v
           

7.判斷一個 list 是否為空

傳統的方式:

if len(mylist):
    # Do something with my list
else:
    # The list is empty
           

由于一個空 list 本身等同于 False,是以可以直接:

if mylist:
    # Do something with my list
else:
    # The list is empty
           

8.周遊 list 的同時擷取索引

傳統的方式:

i = 
for element in mylist:
    # Do something with i and element
    i += 
           

這樣更簡潔些:

for i, element in enumerate(mylist):
    # Do something with i and element
    pass
           

9.list 排序

在包含某元素的清單中依據某個屬性排序是一個很常見的操作。例如這裡我們先建立一個包含 person的list:

class Person(object):
    def __init__(self, age):
        self.age = age

persons = [Person(age) for age in (, , )]
           

傳統的方式是:

def get_sort_key(element):
    return element.age

for element in sorted(persons, key=get_sort_key):
    print "Age:", element.age
           

更加簡潔、可讀性更好的方法是使用 Python 标準庫中的 operator 子產品。attrgetter 方法優先傳回讀取的屬性值作為參數傳遞給 sorted 方法。operator 子產品還包括 itemgetter 和 methodcaller 方法,作用如其字面含義。

from operator import attrgetter

for element in sorted(persons, key=attrgetter('age')):
    print "Age:", element.age
           

10.在 Dictionary 中元素分組

class Person(object):
    def __init__(self, age):
        self.age = ageperson

s = [Person(age) for age in (, , , , )]
           

如果現在我們要按照年齡分組的話,一種方法是使用 in 操作符:

persons_by_age = {}

for person in persons:
    age = person.age
    if age in persons_by_age:
        persons_by_age[age].append(person)
    else:
        persons_by_age[age] = [person]

assert len(persons_by_age[]) == 
           

相比較之下,使用 collections 子產品中 defaultdict 方法的途徑可讀性更好。defaultdict 将會利用接受的參數為每個不存在的 key 建立對應的值,這裡我們傳遞的是 list,是以它将為每個 key 建立一個 list 類型的值:

from collections import defaultdict

persons_by_age = defaultdict(list)
for person in persons:
    persons_by_age[person.age].append(person)