本文為極客時間 Python核心技術與實戰 專欄的學習筆記
字典
在 Python3.7+,字典被確定為有序(注意:在 3.6 中,字典有序是一個 implementation detail,在 3.7 才正式成為語言特性,因此 3.6 中無法 100% 確保其有序性),而 3.6 之前是無序的,其長度大小可變,元素可以任意地刪減和改變。
相比列表和元組,字典性能更優,可以在常數時間復雜度O(1)內完成查找、添加、刪除操作。
常用創建方法
>>> d1 = {'name': 'Json', 'age': 20, 'gender': 'male'}
>>> d2 = dict( {'name': 'Json', 'age': 20, 'gender': 'male'})
>>> d3 = dict([('name', 'Json'),('age', 20),('gender','male')])
>>> d4 = dict(name='Json', age=20, gender='male')
>>> d1 == d2 == d3 == d4
True
索引
使用dict[key]
格式索引,如果不存在,會拋出KeyError
異常。
>>> d1['name']
'Json'
>>> d1['location']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'location'
使用get(key, default)
方法不會拋出異常,此外當該鍵不存在時,可以指定返回的默認值。
>>> d1.get('name')
'Json'
>>> d1.get('location')
>>> d1.get('location', None)
None
判斷某元素是否是字典的鍵:
>>> 'name' in d1 # 同d1.keys()等價
True
>>> 'name' in d1.keys()
True
>>> 'Json' in d1
False
>>> 'Json' in d1.values()
True
添加
>>> d = {'name': 'jason', 'age': 20}
>>> d['gender'] = 'male'
>>> d
{'name': 'jason', 'age': 20, 'gender': 'male'}
刪除
>>> d.pop('gender')
'male'
>>> d
{'name': 'jason', 'age': 20}
排序
根據鍵排序
>>> d = {'b': 1, 'v': 20, 'a': 17}
>>> d_sorted_by_key = sorted(d.items(), key=lambda x: x[0])
>>> d_sorted_by_key
[('a', 17), ('b', 1), ('v', 20)]
>>> d
{'b': 1, 'v': 20, 'a': 17}
根據值排序
>>> d_sorted_by_value = sorted(d.items(), key = lambda x: x[1])
>>> d_sorted_by_value
[('b', 1), ('a', 17), ('v', 20)]
性能分析
舉例:有1000萬件產品,產品信息包括:產品ID、價格。現在需求是:給定某件產品的ID,找出其價格:
1.用列表來存儲數據
存儲結構如下:
products = [
(143121312, 100),
(432314553, 30),
(32421912367, 150)
]
那么查找需要遍歷整個列表,時間復雜度為O(n)。即使先對列表排序,然后二分查找,也會需要O(logn)的時間復雜度,並且排序還需要O(nlogn)的時間。
2. 用字典存儲數據
存儲結構如下:
products = {
'143121312': 100,
'432314553': 30,
'32421912367': 150
}
因為字典內部結構是一張哈希表,所以可以在O(1)的時間復雜度內完成查找。
3. 效率對比
import time
import numpy
def find_product_price_list(products, product_id):
for id, price in products:
if id == product_id:
return price
return None
def find_product_price_dict(products, product_id):
for id in products.keys():
if id == product_id:
return products_dict[id]
return None
r = numpy.random.randint(0,10000000,10000000) # 生成10000000個隨機數
id = [str(x) for x in r]
price = [x for x in range(20000000, 30000000)]
products_list = list(zip(id, price))
products_dict = dict(zip(id, price))
# 添加新元素
products_list.append(('111111111', 300)) # 追加到列表末尾
products_dict['111111111'] = 300
start_using_dict = time.perf_counter()
find_product_price_dict(products_dict, '111111111')
end_using_dict = time.perf_counter()
print('time elapse using dict: {}'.format(end_using_dict - start_using_dict))
start_using_list = time.perf_counter()
find_product_price_list(products_dict, '111111111')
end_using_list = time.perf_counter()
print('time elapse using list: {}'.format(end_using_list - start_using_list))
# ===========運行結果============
time elapse using dict: 0.1983588489999999
time elapse using list: 0.41368435999999953
集合
而集合和字典基本相同,唯一的區別,就是集合沒有鍵和值的配對,是一系列無序的、唯一的元素組合。
常用創建方法
>>> s1 = {1,2,3}
>>> s2 = set([1,2,3])
>>> s1 == s2
True
索引
集合並不支持索引操作,因為集合本質上是一個哈希表,和列表不一樣。
進行如下操作,Python會拋出TypeError
異常。
>>> s1[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'set' object is not subscriptable
只能判斷某元素是否在集合中:
>>> 1 in s1
True
>>> 10 in s1
False
添加
>>> s = {1,2,3}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
刪除
>>> s.remove(4)
>>> s
{1, 2, 3}
注意:由於集合是無序的,所以無法確定pop()
方法會刪除哪個元素,所以謹慎使用。一般刪除操作采用remove()
即可。
排序
>>> s = {2,4,546,34}
>>> sorted(s)
[2, 4, 34, 546]
集合運算
常用集合運算
語法 | 操作 | 說明 |
---|---|---|
set(list1) | set(list2) | union | 包含 list1 和 list2 所有數據的新集合 |
set(list1) & set(list2) | intersection | 包含 list1 和 list2 中共同元素的新集合 |
set(list1) - set(list2) | difference | 在 list1 中出現但不在 list2 中出現的元素的集合 |
性能分析
還是以上面那個例子為例,現在要求計算出有多少種價格。為了節省時間,我們把產品數量降低到10萬。
查找效率
1. 用列表存儲數據
需要兩層循環。那么,在最差情況下,需要 O(n^2) 的時間復雜度。
2. 用集合存儲數據
由於集合是高度優化的哈希表,里面元素不能重復,並且其添加和查找操作只需 O(1) 的復雜度,那么,總的時間復雜度就只有 O(n)。
3. 效率對比
import time
import numpy
def find_unique_price_set(products):
unique_price_set = set()
for _, price in products:
unique_price_set.add(price)
return len(unique_price_set)
def find_unique_price_list(products):
unique_price_list = []
for _, price in products: # A
if price not in unique_price_list: #B
unique_price_list.append(price)
return len(unique_price_list)
r = numpy.random.randint(0,1000000,100000) # 生成100000個隨機數
id = [str(x) for x in r]
price = [x for x in range(200000, 300000)]
products = list(zip(id, price))
start_using_set = time.perf_counter()
find_unique_price_set(products)
end_using_set = time.perf_counter()
print('time elapse using set: {}'.format(end_using_set - start_using_set))
start_using_list = time.perf_counter()
find_unique_price_list(products)
end_using_list = time.perf_counter()
print('time elapse using list: {}'.format(end_using_list - start_using_list))
# ===========運行結果============
time elapse using set: 0.00985934799999999
time elapse using list: 65.528253501
可以看出,僅10萬數據,差距就已經很明顯了。
交集、並集、差集運算
以求交集為例:
import time
list_a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 34, 53, 42, 44]
list_b = [2, 4, 6, 9, 23]
intersection = []
# 列表做交集
start_using_list = time.perf_counter()
for a in list_a:
for b in list_b:
if a == b:
intersection.append(a)
end_using_list = time.perf_counter()
print(intersection)
print('time: {}'.format(end_using_list - start_using_list))
# 集合做交集
start_using_list = time.perf_counter()
intersection = list(set(list_a) & set(list_b))
end_using_list = time.perf_counter()
print(intersection)
print('time: {}'.format(end_using_list - start_using_list))
# ===========運行結果============
[2, 4, 6, 9]
time: 9.622000000000797e-06
[9, 2, 4, 6]
time: 4.169000000001782e-06