引言
我們前面的文章介紹了數字和字符串,比如我計算今天一天的開銷花了多少錢我可以用數字來表示,如果是整形用 int ,如果是小數用 float ,如果你想記錄某件東西花了多少錢,應該使用 str 字符串型,如果你想記錄表示所有開銷的物品名稱,你應該用什么表示呢?
可能有人會想到我可以用一個較長的字符串表示,把所有開銷物品名稱寫進去,但是問題來了,如果你發現你記錄錯誤了,想刪除掉某件物品的名稱,那你是不是要在這個長字符串中去查找到,然后刪除,這樣雖然可行,那是不是比較麻煩呢。
這種情況下,你是不是需要Python給我們提供一種新的數據結構,可以存儲很多個字符串,能讓我們方便的添加修改和刪除,就完美了。
列表(list)同字符串一樣都是有序的,因為他們都可以通過切片和索引進行數據訪問,列表是可變(mutable)的,你可以修改、更新和刪除。
列表是一組有序項目的集合 ,可變的數據類型可 進行增刪改查 ; 列表中可以包含Python中任何數據類型和對象,也可包含另一個列表 可任意組合嵌套 列表是以方括號 [] 包圍的數據集合,不同成員以 , 分隔,列表可通過序號訪問其中成員。
列表可以裝入Python中所有的對象,看下面的例子就知道:
all_list = [ 'nock', # 字符串 1, # 整數 2.0, # 浮點數 print('hello'), # 函數 True, # 布爾值 None, # 空值 [1, 2], # 列表 (3,4), # 元組 {'name': 'nock', 'age': 18} # 字典 ]
列表的定義和創建
定義: [] 內以逗號分隔,按照索引,存放各種數據類型,每個位置代表一個元素
列表的創建:
第一種:
fruit = ['pineapple', 'pear']
第二種:
fruit = list(['pineapple', 'pear'])
其他數據類型轉為列表:
1、把一個字符串轉化成列表
>>> alphabet = 'abcd' >>> alphabet_list = list(alphabet) >>> alphabet_list ['a', 'b', 'c', 'd']
list在把字符串轉換成列表的時候,會把字符串用for循環迭代一下,然后把每個值當作list的一個元素。
2、把元組轉換成列表
>>> jobs = ('pm', 'dev', 'qa', 'ops') >>> jobs_list = list(jobs) >>> type(jobs_list)>>> jobs_list ['pm', 'dev', 'qa', 'ops']
3、把字典轉成列表
>>> age = {'tom': 15, 'jim': 18, 'jerry': 20} >>> age_list = list(age) >>> type(age_list)>>> age_list ['jim', 'jerry', 'tom'] >>> values_list = list(age.values()) >>> values_list [18, 20, 15]
list在把字典轉換成列表的時候,默認循環的是字典的key,所以會把key當作列表的元素;如果指定循環的是values,那么就會把values當作列表的元素。
列表的特點和常用方法
特征:
- 多值: 可存放多個值
- 有序: 按照從左到右的順序定義列表元素,下標從0開始順序訪問
3.可變: 可修改指定索引位置對應的值
列表的增刪改查:
增加操作:
# 增 插入 可插入到任何位置 >>> fruit = ['pineapple', 'pear'] >>> fruit.insert(1, 'grape') >>> fruit ['pineapple', 'grape', 'pear']
在使用 insert 方法的時候,必須要指定列表中要插入的新元素的位置,插入元素的實際位置是在 指定位置元素的前面的
位置 ,如果指定插入的位置在列表中不存在,實際上也就是超出指定列表的長度,程序運行不會報錯,但是這個元素一定會被放到這個列表的最后位置。
>>> fruit = ['pineapple', 'pear'] >>> fruit.insert(4, 'grape') >>> fruit ['pineapple', 'pear', 'grape'] # 增 append方法 數據會追加到尾部 >>> fruit = ['pineapple', 'pear'] >>> fruit.append('grape') >>> fruit ['pineapple', 'pear', 'grape']
# 合并 extend 把一個列表的值合并到當前一個列表中 >>> fruit_one = ['banana', 'apple', 'orange'] >>> fruit_two = ['pineapple', 'grape', 'pear'] >>> fruit_one.extend(fruit_two) >>> fruit_one ['banana', 'apple', 'orange', 'pineapple', 'grape', 'pear']
刪除操作:
# del 直接刪除 >>> jobs = ['PM', 'UI', 'QA', 'OPS'] >>> del jobs[0] >>> jobs ['UI', 'QA', 'OPS'] # remove 根據remove方法, >>> jobs = ['PM', 'UI', 'QA', 'OPS'] >>> jobs.remove('PM') >>> jobs ['UI', 'QA', 'OPS'] # pop 默認刪除列表最后一個元素 >>> jobs = ['PM', 'UI', 'QA', 'OPS'] >>> jobs.pop() # pop方法,默認刪除最后一個,返回刪除元素 'OPS' >>> jobs ['PM', 'UI', 'QA'] >>> help(jobs.pop) Help on built-in function pop: pop(...) method of builtins.list instance L.pop([index]) -> item -- remove and return item at index (default last). Raises IndexError if list is empty or index is out of range. >>> jobs.pop(1) # pop還可以指定元素下標,指定刪除 'UI' >>> jobs ['PM', 'QA'] # clear 方法清空一個列表 >>> jobs = ['PM', 'UI', 'QA', 'OPS'] >>> jobs.clear() >>> jobs []
remove方法刪除一個元素,必須是在列表中的,否則會報錯,del利用下標來刪除元素,pop默認刪除最后一個元素,也可以指定元素下標來刪除。
修改操作:
>>> jobs = ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs[2] = 'QA' # 把下標為2的元素替換成QA,根據下標然后給元素重新賦值 >>> jobs ['PM', 'UI', 'QA', 'OPS', 'DBA', 'DEV'] >>> jobs[-2] = 'Sales' # 把下標為12的元素替換成Sales,根據下標然后給元素重新賦值 >>> jobs ['PM', 'UI', 'QA', 'OPS', 'Sales', 'DEV']
查詢操作:
>>> jobs = ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs[1] 'UI' >>> jobs[2] 'UE' >>> jobs[4] 'DBA' >>> jobs[-2] # 還可以倒數著來,不過下標從-1開始 'DBA'
列表索引:
>>> jobs = ['PM', 'UI', 'OPS', 'UE', 'OPS', 'DBA', 'DEV', 'UE'] >>> jobs.index('OPS') 2 >>> jobs.index('UE') 3 >>> jobs.index('xx') Traceback (most recent call last): File "", line 1, in ValueError: 'xx' is not in list >>> if 'OPS' in jobs: ... print(jobs.index('OPS')) ... 2
索引下標,只會返回第一個元素的下標,如果元素不在列表中,會報錯,我們可以利用 in 這個關鍵之來判斷元素是否在列表中。
列表切片:
>>> jobs = ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs[1:4] # 取下標從1到4的元素,但是不包括4,列表切片的特征就是左開右閉,也就是左取右棄。 ['UI', 'UE', 'OPS'] >>> jobs[1:-1] # 取下標為1到-1的元素,不包括-1,也就是最后一個元素不會被取出來。 ['UI', 'UE', 'OPS', 'DBA'] >>> jobs[:] # 這個在切片符左右沒有下標限制,所以就是代表全取 ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs[::] # 效果和上面一樣,但是你會發現有兩切片符,這是因為切片有一個步長的概念 ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs[0:3] # 取下標0到3的元素,但不包括3 ['PM', 'UI', 'UE'] >>> jobs[:3] # 和上面效果一樣 ['PM', 'UI', 'UE'] >>> jobs[3:] # 從下標3開始,到最后一個元素 ['OPS', 'DBA', 'DEV'] >>> jobs[3:-1] # 從下標3開始,到最后一個元素,但是不包括最后一個元素 ['OPS', 'DBA'] >>> jobs[0::2] # 從下標0開始,按照2個步長取值 ['PM', 'UE', 'DBA'] >>> jobs[::2] # 和上面效果一樣 ['PM', 'UE', 'DBA']
利用下標取出的一個單獨元素是str類型,而利用分片取出的是一個list類型。
列表元素統計:
>>> jobs = ['PM', 'UI', 'OPS', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs.count('OPS') # 因為列表是有序的一種數據類型,所以它的元素是可以重疊的,所以有元素統計。 2
列表排序和翻轉:
>>> jobs = ['PM', 'UI', 'OPS', 'UE', 'OPS', 'DBA', 'DEV', 1, 2, 3] >>> jobs.sort() Traceback (most recent call last): File "", line 1, in TypeError: unorderable types: int() < str() # Python3.0里不同數據類型不能放在一起排序了,擦 >>> jobs[-1] = '3' >>> jobs[-2] = '2' >>> jobs[-3] = '1' >>> jobs ['DBA', 'DEV', 'OPS', 'OPS', 'PM', 'UE', 'UI', '1', '2', '3'] >>> jobs.sort() >>> jobs ['1', '2', '3', 'DBA', 'DEV', 'OPS', 'OPS', 'PM', 'UE', 'UI'] >>> jobs.append('#') >>> jobs.append('&') >>> jobs.sort() >>> jobs ['#', '&', '1', '2', '3', 'DBA', 'DEV', 'OPS', 'OPS', 'PM', 'UE', 'UI'] # 可以看出排序的順序 特殊字符->數字->字母 這么一個優先級 >>> jobs.reverse() # 翻轉最后到最前面 >>> jobs ['UI', 'UE', 'PM', 'OPS', 'OPS', 'DEV', 'DBA', '3', '2', '1', '&', '#']
sort() 方法會修改原列表,而不是創建一個新的有序列表, reverse() 也會修改原列表,但是你希望排序,但是又不希望修改原列表,你只能利用Python中一個名為 sorted() 的內置函數來操作:
>>> jobs = ['UI', 'UE', 'PM', 'OPS', 'OPS', 'DEV', 'DBA', '3', '2', '1', '&', '#'] >>> newlist = sorted(jobs) >>> jobs ['UI', 'UE', 'PM', 'OPS', 'OPS', 'DEV', 'DBA', '3', '2', '1', '&', '#'] >>> newlist ['#', '&', '1', '2', '3', 'DBA', 'DEV', 'OPS', 'OPS', 'PM', 'UE', 'UI']
列表拷貝:
>>> jobs ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs_copy = jobs.copy() # 復制一份jobs列表 >>> jobs_copy ['PM', 'UI', 'UE', 'OPS', 'DBA', 'DEV'] >>> jobs = ['PM', 'UI', 'UE', 'OPS', ['DBA', 'QA', 'DEV']] # 嵌入式列表 >>> jobs_copy2 = jobs.copy() >>> jobs_copy2 ['PM', 'UI', 'UE', 'OPS', ['DBA', 'QA', 'DEV']] >>> jobs[0] = 'HR' # 改變小標為0的元素 >>> jobs ['HR', 'UI', 'UE', 'OPS', ['DBA', 'QA', 'DEV']] # 改變了 >>> jobs_copy2 ['PM', 'UI', 'UE', 'OPS', ['DBA', 'QA', 'DEV']] # 沒變 >>> jobs[-1][0] = 'Sales' # 改變內嵌列表的下標為0的元素 >>> jobs ['HR', 'UI', 'UE', 'OPS', ['Sales', 'QA', 'DEV']] # 改變了 >>> jobs_copy2 ['PM', 'UI', 'UE', 'OPS', ['Sales', 'QA', 'DEV']] # 改變了
從上面可以看出列表的copy方法是一個淺copy的栗子,只會拷貝第一次,而多層嵌入的話,會隨著源列表的變化為變化,關于深拷貝和淺拷貝后面詳細介紹。
列表所有的方法如下:
class list(object): """ list() -> new empty list list(iterable) -> new list initialized from iterable's items """ def append(self, p_object): # real signature unknown; restored from __doc__ """ L.append(object) -> None -- append object to end """ pass def clear(self): # real signature unknown; restored from __doc__ """ L.clear() -> None -- remove all items from L """ pass def copy(self): # real signature unknown; restored from __doc__ """ L.copy() -> list -- a shallow copy of L """ return [] def count(self, value): # real signature unknown; restored from __doc__ """ L.count(value) -> integer -- return number of occurrences of value """ return 0 def extend(self, iterable): # real signature unknown; restored from __doc__ """ L.extend(iterable) -> None -- extend list by appending elements from the iterable """ pass def index(self, value, start=None, stop=None): # real signature unknown; restored from __doc__ """ L.index(value, [start, [stop]]) -> integer -- return first index of value. Raises ValueError if the value is not present. """ return 0 def insert(self, index, p_object): # real signature unknown; restored from __doc__ """ L.insert(index, object) -- insert object before index """ pass def pop(self, index=None): # real signature unknown; restored from __doc__ """ L.pop([index]) -> item -- remove and return item at index (default last). Raises IndexError if list is empty or index is out of range. """ pass def remove(self, value): # real signature unknown; restored from __doc__ """ L.remove(value) -> None -- remove first occurrence of value. Raises ValueError if the value is not present. """ pass def reverse(self): # real signature unknown; restored from __doc__ """ L.reverse() -- reverse *IN PLACE* """ pass def sort(self, key=None, reverse=False): # real signature unknown; restored from __doc__ """ L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE* """ pass
列表推導式:
列表推導式(又稱列表解析式)提供了一種簡明扼要的方法來創建列表,它的語法簡單,很有實用價值。
它的結構是在一個中括號里包含一個表達式,然后是一個for語句,然后是0個或多個for或者if語句。那個表達式可以是任意的,意思是你可以在列表中放入任意類型的對象。返回結果將是一個新的列表,在這個以if和for語句為上下文的表達式運行完成之后產生。
列表解析的一般形式:
[expr for item in itratorable] L = [x**2 for x in range(10)] print(L) Result: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
列表解析返回的是列表, 列表的內容是表達式執行的結果.
[expr for item in iterable if cond] [x ** 0.5 for x in range(10) if x % 2 == 0] [0.0, 1.4142135623730951, 2.0, 2.449489742783178, 2.8284271247461903] [expr for item in iterable if cond1 if cond2] [x for x in range(10) if x % 2 == 0 if x > 1] [2, 4, 6, 8] [expr for item1 in iterable1 for item2 in iterable2] [(x, y) for x in range(10) for y in range(10) if (x+y) %2 == 0]
列表解析用于對可迭代對象做過濾和轉換,返回值是列表.
特性一:代碼變短,可讀性更好
從上圖代碼示例中我們明顯可以看出,列表推導式相比常規方法,寫出來的代碼更加符合pythonic,更加簡短,可讀性更好。
有些人甚至更喜歡使用它而不是filter函數生成列表,但是當你使用列表推導式效果會更加,列表推導式在有些情況下超贊,特別是當你需要使用for循環來生成一個新列表.
特征二:推導式速度更快
#!/usr/bin/env python3 # author: nock import timeit lst = list(range(10)) # 常規方法 def origin(lst): plus_one = [] for i in lst: plus_one.append(i + 1) return plus_one # 列表推導式 def fast(lst): return [ x + 1 for x in lst ] otime = timeit.timeit('origin(range(10))', globals=globals()) print("func origin exec time is {0}".format(otime)) ftime = timeit.timeit('fast(range(10))', globals=globals()) print("func origin exec time is {0}".format(ftime))
結果:
func origin exec time is 2.1059355960023822 func origin exec time is 1.6507169340038672
如果你使用map或者filter結合lambda生成列表,也是沒有列表推導式速度快的,有興趣的可以自己Coding一下。
列表的遍歷
在Python中常用循環對象來遍歷列表,在這里for循環自動調用 next() 方法,將該方法的返回值賦予給循環對象。循環檢測到StopIteration的時候才結束。相對于序列,用循環對象的好處在于:不用在循環還沒有開始的時候,就生成好要使用的元素。所使用的元素可以在循環過程中逐次生成。這樣,節省了空間,提高了效率,編程更靈活。
1. for循環遍歷
#!/usr/bin/env python3 map_list = ['China', 'America', 'Japan', 'Korea'] for countries in map_list: print(countries) # 自動調用迭代器,自動檢測StopIteration # 在上面的程序中,無法知道當前訪問元素的索引,于是有如下代碼: for index in range(len(map_list)): print("key is %s index is %s" % (map_list[index], index))
2. while循環遍歷
#!/usr/bin/env python3 map_list = ['China', 'America', 'Japan', 'Korea'] index = 0 while index < len(map_list): print(index, map_list[index]) index+=1
3. 拉鏈(zip)方法遍歷
#!/usr/bin/env python3 map_list = ['China', 'America', 'Japan', 'Korea'] for index, value in zip(range(len(map_list)), map_list): print(index, value)
4. 利用Python內置函數 enumerate() 列舉
enumerate(iterable [, start ]) 返回枚舉對象, 參數:
iterable: 一個序列、迭代器或其他支持迭代的對象 start: 下標起始位置 #!/usr/bin/env python3 map_list = ['China', 'America', 'Japan', 'Korea'] for value in enumerate(map_list): print(value)
5. 使用 iter() 迭代器
iter(collection [, sentinel ]) 函數用來生成迭代器,返回迭代對象, 參數:
collection: 支持迭代的集合對象
sentinel: 如果傳遞了第二個參數,則參數 object 必須是一個可調用的對象(如,函數),此時, iter 創建了一個迭代器對象,每次調用這個迭代器對象的 __next__() 方法時,都會調用object。
#!/usr/bin/env python3 map_list = ['China', 'America', 'Japan', 'Korea'] for value in iter(map_list): print(value)
由于列表在Python內部的組成方式不同于C語言等,其索引的效率相對較為低下。因此在使用python的過程中,如果需要同時用到序號和元素,最好使用enumerate();當我們不需要使用序號時,在列表上直接進行迭代效率最高。
元組
元組其實跟列表差不多,也是存一組數,只不是它一旦創建,便不能再修改,所以又叫只讀列表。
語法: names = ('tom', 'jack', 'andy')
它只有2個方法,一個是count,一個是index:
class tuple(object): """ tuple() -> empty tuple tuple(iterable) -> tuple initialized from iterable's items If the argument is a tuple, the return value is the same object. """ def count(self, value): # real signature unknown; restored from __doc__ """ T.count(value) -> integer -- return number of occurrences of value """ return 0 def index(self, value, start=None, stop=None): # real signature unknown; restored from __doc__ """ T.index(value, [start, [stop]]) -> integer -- return first index of value. Raises ValueError if the value is not present. """ return 0
列表幾種高階常用場景
1. 解壓列表賦值給多個變量
現在有一個包含 N個元素 的元組或者列表,怎樣將它里面的值解壓后同時賦值給 N 個變量?
任何的序列(或者是可迭代對象)可以通過一個簡單的賦值語句解壓并賦值給多個變量。 唯一的前提就是變量的數量必須跟序列元素的 數量一致 的。
代碼示例:
>>> jobs = ('hr', 'dev', 'ops') >>> x, y, z = jobs >>> print(x, y, z) hr dev ops >>> data = ['nock', 8, 24, (2001, 12, 28)] >>> name, shares, size, date = data >>> print(name, shares, size, date) nock 8 24 (2001, 12, 28) >>> date (2001, 12, 28) >>> name, shares, size, (year, mon, day) = data >>> name 'nock' >>> print(year, mon, day) 2001 12 28
如果變量個數和列表元素的個數不匹配,會產生異常的哦。
代碼示例:
>>> jobs = ('hr', 'dev', 'ops') >>> x, y = jobs Traceback (most recent call last): File "", line 1, in ValueError: too many values to unpack (expected 2) >>> x, y, z, x = jobs Traceback (most recent call last): File " ", line 1, in ValueError: not enough values to unpack (expected 4, got 3)
實際上,這種解壓賦值可以用在任何可迭代對象上面,而不僅僅是列表或者元組。 包括字符串,文件對象,迭代器和生成器。
代碼示例:
>>> name = 'Jim' >>> a, b, c = name >>> a 'J' >>> b 'i'
有時候,你可能只想解壓一部分,丟棄其他的值。對于這種情況 Python 并沒有提供特殊的語法。 但是你可以使用任意變量名去占位,到時候丟掉這些變量就行了。
>>> jobs = [18, 30000, 'duck', 100, (2000, 2, 18)] >>> age, wage, _, num, _ = jobs >>> age 18 >>> wage 30000 >>> _ (2000, 2, 18)
你必須保證你選用的那些占位變量名在其他地方沒被使用到。
2. 刪除列表中相同元素并保持順序
怎樣讓一個列表保持元素順序的同時消除重復的值,如果列表上的值都是 hashable 類型,那么可以很簡單的利用集合或者生成器來解決這個問題,比如:
def dedupe(items): seen = set() for item in items: if item not in seen: yield item seen.add(item)
下面是使用上述函數的例子:
>>> def dedupe(items): ... seen = set() ... for item in items: ... if item not in seen: ... yield item ... seen.add(item) ... >>> nums = [1, 5, 2, 1, 9, 1, 5, 10] >>> list(dedupe(nums)) [1, 5, 2, 9, 10]
這個方法僅僅在序列中元素為 hashable 的時候才管用。 如果你想消除元素不可哈希(比如 dict 類型)的序列中重復元素的話,你需要將上述代碼稍微改變一下,就像這樣:
def dedupe(items, key=None): seen = set() for item in items: val = item if key is None else key(item) if val not in seen: yield item seen.add(val)
這里的key參數指定了一個函數,將序列元素轉換成 hashable 類型。下面是它的用法示例:
>>> a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}] >>> list(dedupe(a, key=lambda d: (d['x'],d['y']))) [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}] >>> list(dedupe(a, key=lambda d: d['x'])) [{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
如果你想基于單個字段、屬性或者某個更大的數據結構來消除重復元素,第二種方案同樣可以勝任。
如果你僅僅就是想消除重復元素,通常可以簡單的構造一個集合。比如:
>>> a = [1, 5, 2, 1, 9, 1, 5, 10] >>> list(set(a)) [1, 2, 10, 5, 9]
然而,這種方法不能維護元素的順序,生成的結果中的元素位置被打亂,而上面的方法可以避免這種情況。
我們使用了生成器函數讓我們的函數更加通用,不僅僅是局限于列表處理。 比如,如果如果你想讀取一個文件,消除重復行,你可以很容易像這樣做:
with open(somefile,'r') as f: for line in dedupe(f): ...
上述key函數參數模仿了 sorted() , min() 和 max() 等內置函數的相似功能。
3. 統計列表中出現次數最多的元素
怎樣找出一個列表中出現次數最多的元素呢, collections.Counter 類就是專門為這類問題而設計的, 它甚至有一個有用的 most_common() 方法直接給了你答案。
為了演示,先假設你有一個單詞列表并且想找出哪個單詞出現頻率最高。你可以這樣做:
>>> from collections import Counter >>> words = [ ... 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', ... 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', ... 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', ... 'my', 'eyes', "you're", 'under' ... ] >>> >>> word_counts = Counter(words) >>> top_three = word_counts.most_common(3) >>> print(top_three) [('eyes', 8), ('the', 5), ('look', 4)]
作為輸入, Counter 對象可以接受任意的由可哈希( hashable )元素構成的序列對象。 在底層實現上,一個 Counter 對象就是一個字典,將元素映射到它出現的次數上, 比如:
>>> word_counts['not'] 1 >>> word_counts['eyes'] 8
如果你想手動增加計數,可以簡單的用加法:
>>> morewords = ['why','are','you','not','looking','in','my','eyes'] >>> for word in morewords: ... word_counts[word] += 1 ... >>> word_counts['eyes'] 9
或者你可以使用 update() 方法:
word_counts.update(morewords)
Counter 實例一個鮮為人知的特性是它們可以很容易的跟數學運算操作相結合,比如:
>>> a = Counter(words) >>> b = Counter(morewords) >>> a Counter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2, "you're": 1, "don't": 1, 'under': 1, 'not': 1}) >>> b Counter({'eyes': 1, 'looking': 1, 'are': 1, 'in': 1, 'not': 1, 'you': 1, 'my': 1, 'why': 1}) >>> # Combine counts >>> c = a + b >>> c Counter({'eyes': 9, 'the': 5, 'look': 4, 'my': 4, 'into': 3, 'not': 2, 'around': 2, "you're": 1, "don't": 1, 'in': 1, 'why': 1, 'looking': 1, 'are': 1, 'under': 1, 'you': 1}) >>> # Subtract counts >>> d = a - b >>> d Counter({'eyes': 7, 'the': 5, 'look': 4, 'into': 3, 'my': 2, 'around': 2, "you're": 1, "don't": 1, 'under': 1})
毫無疑問, Counter 對象在幾乎所有需要制表或者計數數據的場合是非常有用的工具。 在解決這類問題的時候你應該優先選擇它,而不是手動的利用字典去實現。
4. 過濾列表元素
你有一個數據列表,想利用一些規則從中提取出需要的值或者是縮短列表,最簡單的過濾序列元素的方法就是使用列表推導,比如:
>>> nums = [1, 2, -1, 4, 100, -2] >>> [n for n in nums if n > 0] [1, 2, 4, 100] >>> [n for n in nums if n < 0] [-1, -2]
使用列表推導的一個潛在缺陷就是如果輸入非常大的時候會產生一個非常大的結果集,占用大量內存。 如果你對內存比較敏感,那么你可以使用生成器表達式迭代產生過濾的元素,比如:
>>> nums = [1, 2, -1, 4, 100, -2] >>> num = (n for n in nums if n < 0) >>> numat 0x102210a50> >>> for n in num: ... print(n) ... -1 -2
有時候,過濾規則比較復雜,不能簡單的在列表推導或者生成器表達式中表達出來。 比如,假設過濾的時候需要處理一些異常或者其他復雜情況。這時候你可以將過濾代碼放到一個函數中, 然后使用內建的 filter() 函數,示例如下:
values = ['1', '2', '-3', '-', '4', 'N/A', '5'] def is_int(val): try: x = int(val) return True except ValueError: return False ivals = list(filter(is_int, values)) print(ivals) # Outputs ['1', '2', '-3', '4', '5']
filter() 函數創建了一個迭代器,因此如果你想得到一個列表的話,就得像示例那樣使用 list() 去轉換。
列表推導和生成器表達式通常情況下是過濾數據最簡單的方式。 其實它們還能在過濾的時候轉換數據,比如:
>>> mylist = [1, 4, -5, 10, -7, 2, 3, -1] >>> import math >>> [math.sqrt(n) for n in mylist if n > 0] [1.0, 2.0, 3.1622776601683795, 1.4142135623730951, 1.7320508075688772]
過濾操作的一個變種就是將不符合條件的值用新的值代替,而不是丟棄它們。 比如,在一列數據中你可能不僅想找到正數,而且還想將不是正數的數替換成指定的數。 通過將過濾條件放到條件表達式中去,可以很容易的解決這個問題,就像這樣:
>>> mylist = [1, 4, -5, 10, -7, 2, 3, -1] >>> clip_neg = [n if n > 0 else 0 for n in mylist] >>> clip_neg [1, 4, 0, 10, 0, 2, 3, 0] >>> clip_pos = [n if n < 0 else 0 for n in mylist] >>> clip_pos [0, 0, -5, 0, -7, 0, 0, -1]
另外一個值得關注的過濾工具就是 itertools.compress() , 它以一個 iterable 對象和一個相對應的 Boolean 選擇器序列作為輸入參數。 然后輸出 iterable 對象中對應選擇器為 True 的元素。 當你需要用另外一個相關聯的序列來過濾某個序列的時候,這個函數是非常有用的。 比如,假如現在你有下面兩列數據:
addresses = [ '5412 N CLARK', '5148 N CLARK', '5800 E 58TH', '2122 N CLARK', '5645 N RAVENSWOOD', '1060 W ADDISON', '4801 N BROADWAY', '1039 W GRANVILLE', ] counts = [ 0, 3, 10, 4, 1, 7, 6, 1]
現在你想將那些對應 count 值大于5的地址全部輸出,那么你可以這樣做:
>>> from itertools import compress >>> more5 = [n > 5 for n in counts] >>> more5 [False, False, True, False, False, True, True, False] >>> list(compress(addresses, more5)) ['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']
這里的關鍵點在于先創建一個 Boolean 序列,指示哪些元素符合條件。 然后 compress() 函數根據這個序列去選擇輸出對應位置為 True 的元素。
和 filter() 函數類似, compress() 也是返回的一個迭代器。因此,如果你需要得到一個列表,那么你需要使用 list() 來將結果轉換為列表類型。
5. 列表上索引值迭代
你想在迭代一個列表的同時跟蹤正在被處理的元素索引,內置的 enumerate() 函數可以很好的解決這個問題:
>>> my_list = ['a', 'b', 'c'] >>> for idx, val in enumerate(my_list): ... print(idx, val) ... 0 a 1 b 2 c
為了按傳統行號輸出(行號從1開始),你可以傳遞一個開始步長值:
>>> my_list = ['a', 'b', 'c'] >>> for idx, val in enumerate(my_list, 1): ... print(idx, val) ... 1 a 2 b 3 c
這種情況在你遍歷文件時想在錯誤消息中使用行號定位時候非常有用:
def parse_data(filename): with open(filename, 'rt') as f: for lineno, line in enumerate(f, 1): fields = line.split() try: count = int(fields[1]) ... except ValueError as e: print('Line {}: Parse error: {}'.format(lineno, e))
enumerate() 對于跟蹤某些值在列表中出現的位置是很有用的。 所以,如果你想將一個文件中出現的單詞映射到它出現的行號上去,可以很容易的利用 enumerate() 來完成:
word_summary = defaultdict(list) with open('myfile.txt', 'r') as f: lines = f.readlines() for idx, line in enumerate(lines): # Create a list of words in current line words = [w.strip().lower() for w in line.split()] for word in words: word_summary[word].append(idx)
如果你處理完文件后打印 word_summary ,會發現它是一個字典(準確來講是一個 defaultdict ), 對于每個單詞有一個 key ,每個 key 對應的值是一個由這個單詞出現的行號組成的列表。 如果某個單詞在一行中出現過兩次,那么這個行號也會出現兩次, 同時也可以作為文本的一個簡單統計。
當你想額外定義一個計數變量的時候,使用 enumerate() 函數會更加簡單。你可能會像下面這樣寫代碼:
lineno = 1 for line in f: # Process line ... lineno += 1
但是如果使用 enumerate() 函數來代替就顯得更加優雅了:
for lineno, line in enumerate(f): # Process line ...
enumerate() 函數返回的是一個 enumerate 對象實例, 它是一個迭代器,返回連續的包含一個計數和一個值的元組, 元組中的值通過在傳入序列上調用 next() 返回。
還有一點可能并不很重要,但是也值得注意, 有時候當你在一個已經解壓后的元組序列上使用 enumerate() 函數時很容易調入陷阱。 你得像下面正確的方式這樣寫:
data = [ (1, 2), (3, 4), (5, 6), (7, 8) ] # Correct! for n, (x, y) in enumerate(data): ... # Error! for n, x, y in enumerate(data): ...
6. 同時迭代多個列表
你想同時迭代多個列表,每次分別從一個序列中取一個元素, 為了同時迭代多個序列,使用 zip() 函數,比如:
>>> xpts = [1, 5, 4, 2, 10, 7] >>> ypts = [101, 78, 37, 15, 62, 99] >>> for x, y in zip(xpts, ypts): ... print(x,y) ... 1 101 5 78 4 37 2 15 10 62 7 99
zip(a, b) 會生成一個可返回元組 (x, y) 的迭代器,其中x來自a,y來自b。 一旦其中某個序列到底結尾,迭代宣告結束。 因此迭代長度跟參數中最短序列長度一致。
>>> a = [1, 2, 3] >>> b = ['w', 'x', 'y', 'z'] >>> for i in zip(a,b): ... print(i) ... (1, 'w') (2, 'x') (3, 'y')
如果這個不是你想要的效果,那么還可以使用 itertools.zip_longest() 函數來代替,比如:
>>> from itertools import zip_longest >>> for i in zip_longest(a,b): ... print(i) ... (1, 'w') (2, 'x') (3, 'y') (None, 'z') >>> for i in zip_longest(a, b, fillvalue=0): ... print(i) ... (1, 'w') (2, 'x') (3, 'y') (0, 'z')
當你想成對處理數據的時候 zip() 函數是很有用的。 比如,假設你headers列表和一個values列表,就像下面這樣:
headers = ['name', 'shares', 'price'] values = ['ACME', 100, 490.1]
使用zip()可以讓你將它們打包并生成一個字典:
s = dict(zip(headers,values))
或者你也可以像下面這樣產生輸出:
for name, val in zip(headers, values): print(name, '=', val)
雖然不常見,但是 zip() 可以接受多于兩個的序列的參數。 這時候所生成的結果元組中元素個數跟輸入序列個數一樣, 比如:
>>> a = [1, 2, 3] >>> b = [10, 11, 12] >>> c = ['x','y','z'] >>> for i in zip(a, b, c): ... print(i) ... (1, 10, 'x') (2, 11, 'y') (3, 12, 'z')
最后強調一點就是, zip() 會創建一個迭代器來作為結果返回。 如果你需要將結對的值存儲在列表中,要使用 list() 函數,比如:
>>> zip(a, b)>>> list(zip(a, b)) [(1, 10), (2, 11), (3, 12)]
7. 展開嵌透的列表
你想將一個多層嵌套的列表展開成一個單層列表, 可以寫一個包含 yield from 語句的遞歸生成器來輕松解決這個問題。比如:
from collections import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x items = [1, 2, [3, 4, [5, 6], 7], 8] # Produces 1 2 3 4 5 6 7 8 for x in flatten(items): print(x)
在上面代碼中, isinstance(x, Iterable) 檢查某個元素是否是可迭代的。 如果是的話 yield from 就會返回所有子例程的值。最終返回結果就是一個沒有嵌套的簡單列表了。
額外的參數 ignore_types 和檢測語句 isinstance(x, ignore_types) 用來將字符串和字節排除在可迭代對象外,防止將它們再展開成單個的字符。 這樣的話字符串數組就能最終返回我們所期望的結果了。比如:
>>> items = ['Dave', 'Paula', ['Thomas', 'Lewis']] >>> for x in flatten(items): ... print(x) ... Dave Paula Thomas Lewis
語句 yield from 在你想在生成器中調用其他生成器作為子例程的時候非常有用。 如果你不使用它的話,那么就必須寫額外的 for 循環了,比如:
def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): for i in flatten(x): yield i else: yield x
盡管只改了一點點,但是 yield from 語句看上去感覺更好,并且也使得代碼更簡潔清爽。
之前提到的對于字符串和字節的額外檢查是為了防止將它們再展開成單個字符。 如果還有其他你不想展開的類型,修改參數 ignore_types 即可。
最后要注意的一點是 yield from 在涉及到基于協程和生成器的并發編程中扮演著更加重要的角色。
8. 映射名稱到列表元素
你有一段通過下標訪問列表或者元組中元素的代碼,但是這樣有時候會使得你的代碼難以閱讀, 于是你想通過名稱來訪問元素。
?collections.namedtuple()?函數通過使用一個普通的元組對象來幫你解決這個問題。 這個函數實際上是一個返回Python中標準元組類型子類的一個工廠方法。 你需要傳遞一個類型名和你需要的字段給它,然后它就會返回一個類,你可以初始化這個類,為你定義的字段傳遞值等。 代碼示例:
>>> from collections import namedtuple >>> Subscriber = namedtuple('Subscriber', ['addr', 'joined']) >>> sub = Subscriber('jonesy@example.com', '2012-10-19') >>> sub Subscriber(addr='jonesy@example.com', joined='2012-10-19') >>> sub.addr 'jonesy@example.com' >>> sub.joined '2012-10-19'
盡管 namedtuple 的實例看起來像一個普通的類實例,但是它跟元組類型是可交換的,支持所有的普通元組操作,比如索引和解壓。 比如:
>>> len(sub) 2 >>> addr, joined = sub >>> addr 'jonesy@example.com' >>> joined '2012-10-19'
命名元組的一個主要用途是將你的代碼從下標操作中解脫出來。 因此,如果你從數據庫調用中返回了一個很大的元組列表,通過下標去操作其中的元素, 當你在表中添加了新的列的時候你的代碼可能就會出錯了。但是如果你使用了命名元組,那么就不會有這樣的顧慮。
為了說明清楚,下面是使用普通元組的代碼:
def compute_cost(records): total = 0.0 for rec in records: total += rec[1] * rec[2] return total
下標操作通常會讓代碼表意不清晰,并且非常依賴記錄的結構。 下面是使用命名元組的版本:
from collections import namedtuple Stock = namedtuple('Stock', ['name', 'shares', 'price']) def compute_cost(records): total = 0.0 for rec in records: s = Stock(*rec) total += s.shares * s.price return total
命名元組另一個用途就是作為字典的替代,因為字典存儲需要更多的內存空間。 如果你需要構建一個非常大的包含字典的數據結構,那么使用命名元組會更加高效。 但是需要注意的是,不像字典那樣,一個命名元組是不可更改的。比如:
>>> s = Stock('ACME', 100, 123.45) >>> s Stock(name='ACME', shares=100, price=123.45) >>> s.shares = 75 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute
如果你真的需要改變屬性的值,那么可以使用命名元組實例的 _replace() 方法, 它會創建一個全新的命名元組并將對應的字段用新的值取代。比如:
>>> s = s._replace(shares=75) >>> s Stock(name='ACME', shares=75, price=123.45)
_replace() 方法還有一個很有用的特性就是當你的命名元組擁有可選或者缺失字段時候, 它是一個非常方便的填充數據的方法。 你可以先創建一個包含缺省值的原型元組,然后使用 _replace() 方法創建新的值被更新過的實例。比如:
from collections import namedtuple Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time']) # Create a prototype instance stock_prototype = Stock('', 0, 0.0, None, None) # Function to convert a dictionary to a Stock def dict_to_stock(s): return stock_prototype._replace(**s)
下面是它的使用方法:
>>> a = {'name': 'ACME', 'shares': 100, 'price': 123.45} >>> dict_to_stock(a) Stock(name='ACME', shares=100, price=123.45, date=None, time=None) >>> b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'} >>> dict_to_stock(b) Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
最后要說的是,如果你的目標是定義一個需要更新很多實例屬性的高效數據結構,那么命名元組并不是你的最佳選擇。 這時候你應該考慮定義一個包含 slots 方法的類.
總結
以上所述是小編給大家介紹的Python數據類型之列表和元組的方法實例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
