日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

3.Python3标准库--数据结构

發布時間:2023/12/20 python 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 3.Python3标准库--数据结构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?(一)enum:枚舉類型

import enum''' enum模塊定義了一個提供迭代和比較功能的枚舉類型??梢杂眠@個為值創建明確定義的符號,而不是使用字面量整數或字符串 '''

  

1.創建枚舉

import enum''' 可以使用定義一個類,繼承自Enum,來實現枚舉 '''class Color(enum.Enum):red = 1green = 2yellow = 3cyan = 4purple = 5# 此時我們可以直接通過類名來調用里面的元素,里面的元素有兩個屬性,一個是name,一個是value print(Color.red) # Color.red print(Color.red.name) # red print(Color.red.value) # 1

  

2.迭代

import enum''' 可以迭代處理枚舉的各個成員 '''class Color(enum.Enum):red = 1green = 2yellow = 3cyan = 4purple = 5for c in Color:print(c, c.name, c.value)'''Color.red red 1Color.green green 2Color.yellow yellow 3Color.cyan cyan 4Color.purple purple 5''' # 可以看到打印是由順序的,就是我們添加的順序

  

3.比較Enum

import enum''' 枚舉類型只能比較是否相等,不能進行大小判斷 '''class Color(enum.Enum):red = 1green = 2yellow = 3cyan = 4purple = 5print(Color.red == Color.cyan) # False print(Color.red == Color.red) # True print(Color.red is Color.red) # True# 注意:如果進行大小比較,會拋出一個TypeError try:Color.red > Color.cyan except TypeError as err:print(err) # '>' not supported between instances of 'Color' and 'Color'# 但如果我非要比較呢?那就不能繼承Enum了,需要繼承IntEnum class Color(enum.IntEnum):red = 1green = 2yellow = 3cyan = 4purple = 5# 會自動將值進行比較 print(Color.cyan < Color.purple) # True # 會自動將值進行相加 print(Color.red + Color.green) # 3

  

4.唯一枚舉值

import enum''' 有相同值得Enum成員會被處理為同一個對象的別名引用,別名可以避免Enum的迭代器中出現相同的值 '''class Color(enum.Enum):red = 1green = 2yellow = 3cyan = 4purple = 5black = 3for c in Color:print(c.name)'''redgreenyellowcyanpurple''' # 可以看到black并沒有被打印出來,因為它和yellow的值一樣,所以將其當做了yellow的別名 # 至于為什么是black被當做了yellow的別名,而不是yellow被當做black的別名,原因很簡單,因為black在下面 print(Color.yellow is Color.black) # True# 并且這里要提一點,值是可以重復的,只是會被當做別名,但是name是不能重復的,一旦重復,必定報錯 # 那如果我想值也不能重復呢?很簡單, 只需要加上一個裝飾器即可 try:@enum.uniqueclass Color(enum.Enum):red = 1green = 2yellow = 3cyan = 4purple = 5black = 3 except ValueError as err:print(err) # duplicate values found in <enum 'Color'>: black -> yellow

  

(二)collections:容器數據類型

import collections''' collections模塊包含除內置類型list、dict和tuple等意外的其他容器數據類型 '''

1.ChainMap:搜索多個字典

from collections import ChainMap''' ChainMap類一個字典序列,并按照其出現的順序搜索以查找與鍵關聯的值。 ChainMap提供了一個很好的上下文容器,因為可以把它看做成一個棧,棧增長時發生變更,棧收縮時這些變更將被丟棄 ''' d1 = {"a": 12, "b": 22, "c": 33} d2 = {"b": 1, "c": 2, "d": 3} d = ChainMap(d1, d2) for k, v in d.items():print(k, v)'''b 22c 33d 3a 12'''# 可以看到打印的結果是無序的,而且如果多個字典中有相同的key,那么只保留第一次出現的key# 并且ChainMap有一個maps屬性,存儲了要搜索的映射列表。這個列表是可變的。所以可以直接增加新映射,或者改變元素的順序以控制查找和更新行為。 print(d.maps) # [{'a': 12, 'b': 22, 'c': 33}, {'b': 1, 'c': 2, 'd': 3}]# 這是我們存儲的信息,如果在d.maps里面修改了,那么會怎么樣呢? print(d1) # {'a': 12, 'b': 22, 'c': 33} d.maps[0]["a"] = "yoyoyo" # 可以看到d.maps里面存儲的只是一個引用,因此改變之后會影響原來的結果 print(d1) # {'a': 'yoyoyo', 'b': 22, 'c': 33}# 那我如果改變了原來的值,會不會影響d.maps呢?顯然是可以的,畢竟同一個內存地址嘛 d2["d"] = "我屮艸芔茻" print(d.maps) # [{'a': 'yoyoyo', 'b': 22, 'c': 33}, {'b': 1, 'c': 2, 'd': '我屮艸芔茻'}]

  

2.Counter:統計可散列的對象

?

from collections import Counter''' Counter是一個容器,可以計算出序列中每一個元素出現的次數 ''' # 初始化 print(Counter("aabbbc")) # Counter({'b': 3, 'a': 2, 'c': 1}) print(Counter(['a', 'a', 'b', 'b', 'b', 'c'])) # Counter({'b': 3, 'a': 2, 'c': 1}) print(Counter(a=2, b=3, c=1)) # Counter({'b': 3, 'a': 2, 'c': 1})c = Counter("aaabbc") # 表示a出現了三次,b出現了兩次,c出現了一次 print(c) # Counter({'a': 3, 'b': 2, 'c': 1}) # 可以進行填充 c.update("bcd") # 可以看到b和c的值都增加了1,并且出現了d print(c) # Counter({'a': 3, 'b': 3, 'c': 2, 'd': 1})# 訪問計數,Counter對象可以像字典一樣訪問 print(c["a"]) # 3 # 如果訪問一個不存在的key,不會引發KeyError,而是會返回0,表示對象中沒有這個key print(c["mmp"]) # 0# 還可以使用elements進行迭代,會得到Counter對象中的所有元素 print(list(c.elements())) # ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd']# 還可以計算出現最多的元素 # 統計string中前三個出現次數最多的元素 string = "sasaxzsdsadfscxzcasdscxzdfscxsasadszczxczxcsds" c = Counter(string) print(c) # Counter({'s': 13, 'c': 7, 'a': 6, 'x': 6, 'z': 6, 'd': 6, 'f': 2}) print(c.most_common(3)) # [('s', 13), ('c', 7), ('a', 6)]# Counter還可以進行算數操作 c1 = Counter("aabbccc") c2 = Counter("bbbccdd") print(c1) # Counter({'a': 2, 'b': 2, 'c': 3}) print(c2) # Counter({'b': 3, 'c': 2, 'd': 2}) # 如果c1的元素出現在了c2中,就把該元素減去,記住:減的是次數 print(c1 - c2) # Counter({'a': 2, 'c': 1}) ''' a在c1中出現了2次,c2中沒有出現,所有是a: 2。b在c1中出現兩次,在c2中出現3次,所以一減就沒有了。 而c在c1中出現了三次,在c2中出現兩次,所以相減還剩下一次。至于c1沒有的元素就不用管了 '''# 相加就很好理解了 print(c1 + c2) # Counter({'b': 5, 'c': 5, 'a': 2, 'd': 2})# 相交的話,查找公共的元素,并且取次數出現較小的那個 print(c1 & c2) # Counter({'b': 2, 'c': 2})# 并集的話,取較大的,記住不是相加,所以b和c出現的次數不會增加,只是取較大的那個、 print(c1 | c2) # Counter({'b': 3, 'c': 3, 'a': 2, 'd': 2})

  

3.defaultdict:缺少的鍵返回一個默認值

from collections import defaultdict''' 標準字典中有setdefault和get,可以用來獲取key對應的value。 如果key存在,兩者會獲取key對應的value 但如果key不存在,setdefault就會先將key和指定的默認值設置進去,然后返回一個默認值。 而get則只會返回默認值,。不會設置值 example:d = {"a": 1}print(d.get("a", 0)) # 1print(d.setdefault("a", 0)) # 1print(d) # {"a": 1}print(d.get("b", 0)) # 0print(d) # {"a": 1}print(d.setdefault("b", 0)) # 0print(d) # {"a": 1, "b": 0}所以這里相當于執行了兩步操作。先將("b", 0)設置到字典里,然后再獲取defaultdict在初始化的時候就會讓調用者提前指定默認值 '''s = "aabbccdddddee" d1 = {} for c in s:d1.setdefault(c, 0)d1[c] += 1 print(d1) # {'a': 2, 'b': 2, 'c': 2, 'd': 5, 'e': 2}# 如果使用defaultdict的話呢? d2 = defaultdict(int) print(d2["a"]) # 0 d2 = defaultdict(str) print("%r" % d2["a"]) # '' d2 = defaultdict(tuple) print(d2["a"]) # () d2 = defaultdict(list) print(d2["a"]) # [] # 如果獲取不到key,那么會自動輸出傳入類型所對應的零值.能獲取到key,輸入key對應的value值s = "aabbccdddddee" d2 = defaultdict(int) for c in s:'''一開始沒有值,設置為0,然后每來一個值就加上1'''d2[c] += 1 print(d2) # defaultdict(<class 'int'>, {'a': 2, 'b': 2, 'c': 2, 'd': 5, 'e': 2})# 此外還可以自定義,只需要添加一個不需要參數的函數即可,指定一個返回值 d3 = defaultdict(lambda: "default") print(d3["aa"]) # default # 此外還可以添加參數 d4 = defaultdict(lambda: "default", aa="bar") print(d4["aa"]) # bar# 這種字典是如何實現的呢?主要是內部實現了一個__missing__魔法方法 class MyDict(dict):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)def __getitem__(self, item):value = super().__getitem__(item)# 會執行父類的__getitem__方法,如果獲取不到# 會檢測我們是否定義__missing__方法,如果有,執行。沒有,報錯# 所以這里的value就是__missing__方法的返回值return valuedef __missing__(self, key):self[key] = "為什么渣男這么吃香,像我們這樣暖人心的老實人卻不受待見"return self[key]d = MyDict([("a", 3), ("b", 4)]) print(d) # {'a': 3, 'b': 4} print(d["mmm"]) # 為什么渣男這么吃香,像我們這樣暖人心的老實人卻不受待見

  

4.deque:雙端隊列

?

from collections import deque''' 雙端隊列支持從任意一端增加和刪除元素。更為常用的兩種數據結構(即棧和隊列)就是雙端隊列的退化形式,它們的輸入和輸出被限制在某一端 ''' d = deque("abcdefg") print(d) # deque(['a', 'b', 'c', 'd', 'e', 'f', 'g']) print(len(d)) # 7 print(d[0]) # a print(d[-1]) # g# 由于deque是一種序列容器,因此同樣支持list的操作。如:通過索引獲取元素,查看長度,刪除元素,反轉元素等等 # list支持的deque基本上都支持 d.reverse() print(d) # deque(['g', 'f', 'e', 'd', 'c', 'b', 'a']) d.remove("c") print(d) # deque(['g', 'f', 'e', 'd', 'b', 'a'])# 填充元素 # 首先可以像list一樣添加元素,但是deque可以從兩端添加 d.append("yoyoyo") # 默認和list一樣,在尾部添加 d.appendleft("喲喲喲") # 也可以添加在頭部 print(d) # deque(['喲喲喲', 'g', 'f', 'e', 'd', 'b', 'a', 'yoyoyo']) # 還可以使用insert, 如果范圍越界,自動添加在兩端 d.insert(100, "x") print(d) # deque(['喲喲喲', 'g', 'f', 'e', 'd', 'b', 'a', 'yoyoyo', 'x'])# extend,extendleft d1 = [1, 2, 3] d2 = deque([4, 5, 6]) d.extend(d1) print(d) # deque(['喲喲喲', 'g', 'f', 'e', 'd', 'b', 'a', 'yoyoyo', 'x', 1, 2, 3]) d.extendleft(d2) print(d) # deque([6, 5, 4, '喲喲喲', 'g', 'f', 'e', 'd', 'b', 'a', 'yoyoyo', 'x', 1, 2, 3]) # 可以看到extend也支持從左端添加,而且不僅僅可以添加deque,任意序列類型都是可以的。 d.extendleft("我屮艸芔茻") print(d) # deque(['茻', '芔', '艸', '屮', '我', 6, 5, 4, '喲喲喲', 'g', 'f', 'e', 'd', 'b', 'a', 'yoyoyo', 'x', 1, 2, 3]) # 注意添加的順序,我們是從左邊開始添加的,先添加"我",然后"屮"跑到開頭就把"我"擠到右邊了,所以是結果是倒過來的# 那么如果消費deque里面的元素呢? print(d.pop()) # 3 print(d.pop()) # 2 print(d.pop()) # 1 print(d.pop()) # x print(d.popleft()) # 茻 # pop是從右端刪除一個元素,popleft是從左端開始刪除一個元素。但是如果我想pop掉指定的索引的元素,只能用pop函數,傳入索引值即可 # 注意:deque和queue一樣,是線程安全的,是受GIL這把超級大鎖保護的,可以不同的線程中進行消費。 # 如果想清空里面的元素的話,可以像list、dict一樣,使用clear函數 d.clear() print(d) # deque([])# 旋轉 # deque還有一個很用的地方就是可以按任意一個方向進行旋轉,從而跳過某些元素。 # d.rotate(n)-->n大于0,從右邊開始取n個元素放到左邊,n小于0,從左邊取n個元素放到右邊 d = deque(range(10)) print(d) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) d.rotate(2) # 從右邊取2個元素放到左邊,所以8和9被放到了左邊 print(d) # deque([8, 9, 0, 1, 2, 3, 4, 5, 6, 7]) d.rotate(-3) # 從左邊取3個元素放到右邊,所以8、9、0被放到了右邊 print(d) # deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])# 限制隊列的大小 # 我們在初始化一個雙端隊列的時候,還可以限制它的大小 d = deque("abcdefg", maxlen=5) # 我們初始化7個元素,但是指定最大長度只有5,所以前面兩個("a"和"b")就被擠出去了 print(d) # deque(['c', 'd', 'e', 'f', 'g'], maxlen=5) d.appendleft("yoyoyo") # 當我往前面添加元素的時候,后面的就被擠出去了,因為隊列最多只能容納5個元素 print(d) # deque(['yoyoyo', 'c', 'd', 'e', 'f'], maxlen=5)

?

  

5.namedtuple:帶名字字段的元組子類

from collections import namedtuple# 傳入名字,和字段 person = namedtuple("person", ["name", "age", "gender"]) person1 = person(name="mashiro", age=16, gender="f") print(person1) # person(name='mashiro', age=16, gender='f') print(person1.name, person1.age, person1.gender) # mashiro 16 f print(person1[0]) # mashiro ''' 可以看到不僅可以像普通的tuple一樣使用索引訪問,還可以使用像類一樣通過.字段名訪問 '''person2 = person("satori", 16, "f") print(person2) # person(name='satori', age=16, gender='f')''' 注意:這個和普通的元組一樣,是不可以修改的 ''' try:person2.name = "xxx" except AttributeError as e:print(e) # can't set attribute# 非法字段名,不能使用Python的關鍵字 try:girl = namedtuple("女孩們", ["for", "in"]) except ValueError as e:print(e) # Type names and field names cannot be a keyword: 'for'# 如果字段名重復了怎么辦 try:girl = namedtuple("女孩們", ["name", "age", "age"]) except ValueError as e:print(e) # Encountered duplicate field name: 'age'# 如果非要加上重名字段呢,可以設置一個參數 girl = namedtuple("女孩們", ["name", "age", "age"], rename=True) print(girl) # <class '__main__.女孩們'> girl1 = girl("koishi", 15, 15) # 可以看到重復的字段名會按照索引的值,在前面加上一個下劃線。比如第二個age重復,它的索引是多少呢?是2,所以默認幫我們把字段名修改為_2 print(girl1) # 女孩們(name='koishi', age=15, _2=15)# 此外我們所有的字段名都保存在_fields屬性中 print(girl1._fields) # ('name', 'age', '_2')

  

6.OrderDict:記住字典增加鍵的順序

from collections import OrderedDict''' OrderDict是一個字典子類,可以記住字典中增加鍵的順序。 在Python2中,字典是無序的,但在Python3中,字典默認是有序的 ''' d = OrderedDict() d["a"] = "A" d["b"] = "B" d["c"] = "C" for k, v in d.items():print(k, v) ''' a A b B c C ''' # 此外也可以在初始化的時候,添加元素 print(OrderedDict({"a": 1})) # OrderedDict([('a', 1)])# 相等性,對于常規字典來說,只要里面元素一樣便是相等的,不考慮順序。但是對于OrderDict來說,除了元素,順序也要一樣,否則就不相等 d1 = {"a": 1, "b": 2} d2 = {"b": 2, "a": 1} print(d1 == d2) # Trued1 = OrderedDict({"a": 1, "b": 2}) d2 = OrderedDict({"b": 2, "a": 1}) print(d1 == d2) # False# 重排 # 在OrderDict中可以使用move_to_end()將鍵移至序列的起始位置或末尾位置來改變鍵的順序 d3 = OrderedDict({"a": 1, "b": 2, "c": 3, "d": 4}) d3.move_to_end("c") # 表示將key="c"的這個鍵值對移動到末尾 print(d3) # OrderedDict([('a', 1), ('b', 2), ('d', 4), ('c', 3)]) d3.move_to_end("c", last=False) # 表示將key="c"的這個鍵值對移動到行首 print(d3) # OrderedDict([('c', 3), ('a', 1), ('b', 2), ('d', 4)])

  

7.collections.abc:容器的抽象基類

from collections import abc''' abc模塊包含了一些抽象基類,其為Python內置容器數據結構以及collections模塊定義的容器數據結構定義了API。 除了明確地定義不同容器的API,這些抽象基類還可以在調用對象之前用instance()測試一個對象是否支持一個API。 有些類還提供了方法實現,它們可以作為"混入類(mix-in)"構造定制容器,而不必從頭實現每一個方法。 '''

  

(三)數組:固定的數據序列

import array''' array模塊定義了一個序列數據結構,看起來與list很相似,只不過所以成員都必須是相同的數據類型。 支持的類型包括數值類型或其他固定大小的基本類型(比如:字節)array成員的類型代碼 代碼 類型 最小大小(字節) b Int 1 B INT 1 h Signed short 2 H Unsigned short 2 i Signed int 2 I Unsigned int 2 l Signed long 4 L Unsigned long 4 q Signed long long 8 Q Unsigned long long 8 f Float 4 d Double 8 '''

  

1.初始化

import array import binascii''' array被實例初始化時可以提供一個參數來描述允許哪種數據類型,還可以有一個存儲在數組中的初始數據序列。 ''' s = b"this is a array" a = array.array("b", s) print("byte string:", s) # byte string: b'this is a array' print("as array:", a) # as array: array('b', [116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 97, 114, 114, 97, 121]) print("as hex:", binascii.hexlify(a)) # as hex: b'746869732069732061206172726179'

  

2.處理數組

import array''' 與其他Python序列類似,可以采用同樣的方式擴展和處理array ''' a = array.array("i", range(3)) print("初始化:", a) # 初始化: array('i', [0, 1, 2]) a.extend(range(3)) print("擴展:", a) # 擴展: array('i', [0, 1, 2, 0, 1, 2]) print("切片:", a[2: 5]) # 切片: array('i', [2, 0, 1]) print("迭代器:", list(enumerate(a))) # 迭代器: [(0, 0), (1, 1), (2, 2), (3, 0), (4, 1), (5, 2)]

  

3.數組和文件

import array import binascii import tempfile''' 可以使用專門的高效讀寫文件的內置方法將數組的內容寫入文件或從文件讀出數組 ''' a = array.array("i", range(5)) print("A1:", a) # A1: array('i', [0, 1, 2, 3, 4])# 將數組的元素寫到臨時文件當中,那么首先要創建一個臨時文件 output = tempfile.TemporaryFile() # tofile函數會把數組a里面的內容寫到output這個臨時文件當中 a.tofile(output) # 刷新 output.flush()# 讀取數據 # 讀取內容 output.seek(0) raw_data = output.read() print("raw_content:", binascii.hexlify(raw_data)) # raw_content: b'0000000001000000020000000300000004000000'# 將內容讀到數組當中,但是我們剛才讀過一遍了,因此別忘了把指針移到文件的開頭 output.seek(0) # 創建新數組 a2 = array.array("i") # 調用fromfile把f里面的內容寫到數組a2里面去,寫入的長度和數組a的長度一樣 a2.fromfile(output, len(a)) print("A2:", a2) # A2: array('i', [0, 1, 2, 3, 4])# 其中tofile使用tobytes格式化數據,fromfile使用frombytes再轉化為一個數組實例a = array.array("i", range(5)) print("a:", a) # a: array('i', [0, 1, 2, 3, 4]) as_bytes = a.tobytes() print("as_bytes:", binascii.hexlify(as_bytes)) # as_bytes: b'0000000001000000020000000300000004000000'a2 = array.array("i") a2.frombytes(as_bytes) print(a2) # array('i', [0, 1, 2, 3, 4])

  

(四)heapq:堆排序算法

import heapq''' 堆(heap)是一個樹形數據結構,其中子節點與父節點有一種有序關系。 二叉堆(binary heap)可以使用一個有組織的列表或數組表示,其中元素N的子元素位于2*N+1和2*N+2(索引從0開始)。 這種布局允許原地重新組織對,從而不必在增加或刪除元素時重新分配大量內存。 最大堆(max-heap)確保父節點大于或等于其兩個子節點,最小堆(min-heap)要求父節點小于或等于其子節點。Python的heapq實現了一個最小堆 '''

  

import heapq''' 創建堆有兩種方式:heappush()和heapify() ''' ''' 最主要我們可以使用heapq來獲取序列極值 ''' data = [1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6] # 從data中選出3個最大的元素 print(heapq.nlargest(3, data)) # [6, 6, 5] # 從data中選出3個最大的元素,按照什么來選,出現的次數來選,也就是選擇出現次數最多的三個元素,這里顯然都是4 print(heapq.nlargest(3, data, key=lambda x: data.count(x))) # [4, 4, 4]data = {"a": 3, "b": 2, "c": 11, "d": 1, "e": 16} # 選擇三個最大的元素,按照value來選擇 print(heapq.nlargest(3, data, lambda x: data[x])) # ['e', 'c', 'a']# 高效合并有序序列 data1 = "abc" data2 = [1, 2, 3] data3 = {4, 5, 6} data4 = {"k": 1, "m": 2} from itertools import chain data = chain(data1, data2, data3, data4) # 得到的data是一個迭代器 print(list(data)) # ['a', 'b', 'c', 1, 2, 3, 4, 5, 6, 'k', 'm']

  

(五)bisect:維護有序的列表

import bisect''' bisect模塊實現了一個算法來向列表中插入元素,同時仍然保證列表有序 '''

?

  

1.有序插入

import bisect''' 可以使用bisect.insort向一個列表中插入元素 ''' values = [46, 39, 80, 91, 62, 71, 32, 70, 15, 6] # 比如我此刻往values里面插入50這個元素 bisect.insort(values, 50) print(values) # [46, 39, 50, 80, 91, 62, 71, 32, 70, 15, 6] ''' bisect默認是從小到大的,因此46比50小,39比50小,80比50大,因此插在了39和80之間 至于我們的values本身就是無序的,因此不可能會把values變成有序的。只能找一個左邊比要插入的值小,右邊比要插入的值大,然后在這個位置把值插進去 ''' # 我們新建一個列表 l = [] for i in values:bisect.insort(l, i)print("-"*20)print(f"{i} {l}")'''--------------------46 [46]--------------------39 [39, 46]--------------------50 [39, 46, 50]--------------------80 [39, 46, 50, 80]--------------------91 [39, 46, 50, 80, 91]--------------------62 [39, 46, 50, 62, 80, 91]--------------------71 [39, 46, 50, 62, 71, 80, 91]--------------------32 [32, 39, 46, 50, 62, 71, 80, 91]--------------------70 [32, 39, 46, 50, 62, 70, 71, 80, 91]--------------------15 [15, 32, 39, 46, 50, 62, 70, 71, 80, 91]--------------------6 [6, 15, 32, 39, 46, 50, 62, 70, 71, 80, 91]''' ''' 這是一個很簡單的例子,實際上,對于這種數據量來說,直接構建列表完成完成一次排序可能速度會更快。 不過對于長列表而言,使用類似這樣一個插入排序算法可以大大節省時間和內存。 所以bisect維護的是一個有序的序列,如果一個序列已經有序了,我們使用這種方法插入值,比直接插入值再sort的效率更高 '''

  

2.處理重復

import bisect''' 如果插入一個序列中已經出現的值,該怎么辦呢?是插在左邊呢,還是插在右邊呢? bisect提供了兩種做法,bisect.insort_right和bisect.insort_left,從名字也能看出來。 至于bisect.insort默認等于bisect.insort_right,是插在右邊的。這兩種方法沒有什么區別 '''# 不過bisect還有一種用法 values = [1, 3, 5, 7, 11] # 首先我想插入一個元素7,可以使用bisect.insort,但是如果我想獲取7這個元素該插在哪個位置,該怎么辦呢? # 可以使用bisect.bisect print(bisect.bisect(values, 7)) # 4 ''' 結果為4,表示我應該插在索引為4的這個地方,原來索引為4的地方是11 所以當7插進去之后,那么11被擠到了后面,因此正好保證有序 同理還有bisect.bisect_left ''' print(bisect.bisect_left(values, 7)) # 3 ''' 得到的結果為3,至于原因,我想不需要再贅述了 bisect.bisect也等價于bisect.bisect_right '''

  

(六)queue:線程安全的FIFO(先進先出)實現

import queue ''' queue提供了適用于多線程編程的先進先出(FIFO, first-in, first-out)數據結構,可以用來在生產者和消費者線程之間安全地傳遞消息或其他數據 它會為調用者處理鎖定,使多個線程能夠安全且容易地處理同一個Queue實例。因為這也是受GIL全局鎖保護的 Queue的大小(可以容納的元素個數)可能首先,以限制內存使用或處理 '''

  

1.基本的FIFO隊列

import queue''' Queue類實現了一個基本的先進先出的容器。使用put將元素增加到這個序列的一端,使用get從另一端獲取。想象一個管子,兩邊都有口,我從一端塞進去,然后從另一端獲取,先塞進去的會先跑到另一端比如我從左邊塞進去1 2 3 4這個四個元素,1先塞進去的話,那么1肯定在最右邊,那么我從另一邊最先獲取的也是1 -------------4 3 2 1------------- 但如果是棧的話,由于只有一個口,右邊被堵死了,所以即便我從左邊塞進去,我還是要從左邊獲取。因此最先放進去的,要最后才能出來 -------------·| 4 3 2 1 | | -------------· ''' # 調用Queue這個類實例化一個隊列 q = queue.Queue() for i in range(5):# 調用put方法塞進去q.put(i) # 可以查看屬性 # 這里顯示為0是因為我們沒有指定最大容量 print(q.maxsize) # 0 # 我們沒有指定容量,所以默認無限大,只要你內存足夠大。 print(q.full()) # False # 判斷隊列是否為空 print(q.empty()) # False # 查看隊列的長度,也就是看看隊列的肚子里面壞了幾個孩子(裝了幾個元素) # 注意不可以使用len(q)來判斷,因為Queue這個類沒有實現__len__方法 print(q.qsize()) # 5while not q.empty():# 可以看到實現了先進先出,這與我們插進去的順序是一樣的print(q.get(), end=" ") # 0 1 2 3 4# 另外解釋一下這個get里面的參數 ''' def get(self, block=True, timeout=None): 這個block,表示是否阻塞,默認為True,表示如果獲取不到元素,就會一直卡在這個地方,直到隊列里面有數據為止。 如果為False,表示不阻塞,隊列為空獲取不到元素的時候會立即報錯,這個一般是在多線程當中會使用timeout表示超時時間,這個參數有什么用呢? 比如消費者消費數據,但是生產者還沒生產完,所以消費者會卡住,需要生產者生產完數據放到隊列里面去,消費者才能獲取數據繼續往下走 如果生產者不生產了,消費者卡住。設置block=False的話呢,可以避免,但是有可能生產者生產的比較慢,不是說不生產了,這種情況會導致生產者還沒把數據放進去的時候就已經報錯了 所以timeout的作用就來了,表示消費者獲取不到數據的時候,依舊會卡住,但是不會一直卡住,會保持一段時間,如果時間過后還沒有數據的話再報錯。 所以要搭配block=True來使用,如果block=False的話,這個參數就沒有什么意義了,因為獲取不到直接報錯,就沒有timeout什么事了。
同理對于put也是一樣,如果數據滿了,放不進去了,put也提供了block和timeout參數供我們使用 '''

  

2.LIFO隊列

?

import queue''' 與Queue的標準FIFO實現相反,LifoQueue使用了(通常與棧數據結構關聯的)后進先出(LIFO)順序 ''' q = queue.LifoQueue() for i in range(5):q.put(i)while not q.empty():print(q.get(), end=" ") # 4 3 2 1 0 # 可以看到,這個和棧比較類似,是先入后出的

  

3.優先隊列

import queue import functools import threading''' 有些情況下,需要根據隊列中元素的特性來決定這些元素的處理順序,而不是簡單地采用隊列中創建或插入元素的順序。 例如,工資部門的打印作業可能就優先于某個開發人員要想打印的代碼清單。PriorityQueue使用隊列內容的有序順序來決定獲取哪一個元素 '''@functools.total_ordering class Job:def __init__(self, priority, description):self.priority = priorityself.description = descriptionprint("New Job:", description)def __eq__(self, other):try:return self.priority == other.priorityexcept AttributeError:return NotImplementeddef __lt__(self, other):try:return self.priority < other.priorityexcept AttributeError:return NotImplementedq = queue.PriorityQueue() q.put(Job(3, "優先級第3")) q.put(Job(10, "優先級第10")) q.put(Job(1, "優先級第1"))def process_job(q):while q.qsize() > 0:next_job = q.get()print("processing job", next_job.description)q.task_done()process_job(q) workers = [threading.Thread(target=process_job, args=(q, )),threading.Thread(target=process_job, args=(q, )) ] for w in workers:w.start()''' 這個的q.join()和上面的q.task_done()有必要說一下。 首先隊列中有這么一個屬性,在Queue這個類的構造函數中:self.unfinished_tasks = 0 這個表示什么含義呢,表示未完成的任務,也就是說我們每put一次,這個計數就會加上1def put(self, item, block=True, timeout=None):省略。。。self._put(item)self.unfinished_tasks += 1self.not_empty.notify() 使用join的話,是等這個計數變為0才能往下走,否則就會卡住。 因此在我們取出一個元素的時候,要記得調用一下q.task_done()來讓計數減1def task_done(self):with self.all_tasks_done:unfinished = self.unfinished_tasks - 1if unfinished <= 0: ''' q.join() # 輸出結果 ''' New Job: 優先級第3 New Job: 優先級第10 New Job: 優先級第1 processing job 優先級第1 processing job 優先級第3 processing job 優先級第10 '''
# 可以看到會順序打印,為什么?那是因為優先級隊列會自動排序,而我們定義好了排序規則''' 這個例子有多個線程在處理作業,要根據get()時隊列中元素的優先級來處理。 運行消費者線程時,增加到隊列的元素的處理順序取決于線程上下文切換。 '''

  

4.構建一個多線程播客客戶端程序

?

from queue import Queue''' 現在我們將構建一個播客客戶端程序,程序的代碼展示了如何利用多個線程使用Queue類。 這個程序要讀入一個或多個rss提要,對每一個提要的專輯排隊,顯示最新的五集以供下載,并使用多線程并發處理多個下載。 這里沒有提供完備的錯誤處理,所以不能在實際的生產環境中使用,不過這個框架可以作為很好的例子來說明如何使用queue模塊。首先要建立一些操作參數,一般情況下,這些參數都來自用戶輸入(例如,首選項,數據庫等)。 不過在這個例子中,線程數和要獲取的url列表都采用了硬編碼值 ''' import threading import requests from urllib.parse import urlparseimport feedparser # 用來解析rss文本的一個模塊# 設置一些全局變量 num_fetch_threads = 2 # 線程數 enclosure_queue = Queue() feed_urls = ["https://talkpython.fm/episodes/rss" ]def message(s):print(f"{threading.current_thread().name}: {s}")# 函數download_enclosure在工作線程中運行,使用urllib處理下載 def download_enclosures(q):'''這是一個工作線程函數,一個接一個地處理每一個成員。而這些守護線程會進入一個無限循環,只有當主線程結束時才會推出:param q::return:'''while True:message("尋找下一個閉包")url = q.get()# 獲取url最后一個/右面的內容當做文件名filename = url.rpartition("/")[-1]# 下載文件message(f"downloading {filename}")# 下載內容response = requests.get(url)data = response.content# 保存內容到filename當中with open(filename, "wb") as f:f.write(data)# 不要忘記處理完了之后,要task_doneq.task_done()''' 一旦定義了線程的目標函數,接下來便可以啟動工作線程。 download_enclosures處理語句url=q.get()時,會阻塞并等待,直到隊列返回某個結果。 這說明,即使隊列中沒有任何內容,也可以安全地啟動線程 '''# 啟動一些線程 for i in range(num_fetch_threads):worker = threading.Thread(target=download_enclosures,args=(enclosure_queue,),name=f"worker-{i}")worker.setDaemon(True)worker.start()''' 下一步使用feedparser模塊獲取提要內容,并將這些專輯的url入隊。 一旦第一個url增加到隊列,就會有某個工作線程提取這個url,并且還是下載。 這個循環會繼續增加元素,直到這個提要已經被完全消費,工作線程會依次將url出隊以完成下載 '''for url in feed_urls:response = feedparser.parse(url)for entry in response["entries"][: 5]:for enclosure in entry.get("enclosures", []):parsed_url = urlparse(enclosure["url"])message(f"queuing {parsed_url.path.rpartition('/')[-1]}")enclosure_queue.put(enclosure['url'])# 現在我們要使用join等待隊列為空 message("***main thread waiting") enclosure_queue.join() message("***done") # 輸出內容不再顯示

  

(七)struct:二進制數據結構

import struct''' struct模塊包括一些函數,這些函數可以完成字節串與原生Python數據類型(如數字和字符串)之間的轉換 '''

  

1.函數與struct類

import struct''' struct提供了一組處理結構值的模塊級函數,另外還有一個Struct類。 格式指示符有字符串格式轉換為一種編譯表示,這與處理正則表達式的方法類似。 這個轉換會耗費一些資源,所以創建一個Struct實例并在這個實例上調用方法時(不使用模塊級函數)只完成一次轉換,這會更高效類比正則: re.match(pattern, text) 使用這種模塊級別的函數時,會先將pattern進行編譯轉換,這個轉換是耗費資源的因此可以先對pattern進行一個編譯,comp = re.compile(pattern) comp.match(text) 這樣的話就只需要轉換一次,struct也是類似的情況 '''

  

2.打包與解包

import struct''' Struct支持使用格式指示符將數據打包(packing)為字符串,另外支持從字符串解包(unpacking)數據。 格式指示符由表示數據類型的字符和可選的數量及字節序(endianness)指示符構成。 要全面了解目前可支持的數據結構,可以參考標準庫文檔 ''' import binascii# values包含一個整型或長整型,一個兩字節字符串,以及一個浮點數。 values = (1, "ab".encode("utf-8"), 2.7) # 格式指示符中包含的空格用來分割類型指示符,并且在編譯格式時會被忽略 # 使用Struct定義格式,I:整型,2s:兩個字節的字符,f:浮點數,之間使用空格分隔 # 表示打包的數據有三個,分別是整型,兩個字節的字符,以及一個浮點 s = struct.Struct("I 2s f") # 使用s.pack函數進行打包,將values打開傳進去 packed_data = s.pack(*values)# s:Struct對象 print(s) # <Struct object at 0x0000000002924458> # 原始數據values print("原始數據:", values) # 原始數據: (1, b'ab', 2.7) # 打印一下我們的格式,也就是我們傳進去的格式 print("格式化字符:", s.format) # 格式化字符: I 2s f # 查看所用的字節 print("使用:", s.size, "bytes") # 使用: 12 bytes # 查看打包之后的結果 print("打包后的結果:", packed_data) # 打包后的結果: b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@' print("將打包的結果進行轉換:", binascii.hexlify(packed_data)) # 將打包的結果進行轉換: b'0100000061620000cdcc2c40'# 我們傳入values,通過s.pack()得到packed_data,那么我們傳入packed_data,可不可以調用一個函數反過來得到values呢? # 答案是可以的,可以使用s.unpack() # 值得一提的是,這個binascii.hexlify,還有一個相反的函數叫做binascii.unhexlify print(packed_data) # b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@' print(binascii.hexlify(packed_data)) # b'0100000061620000cdcc2c40' print(binascii.unhexlify(binascii.hexlify(packed_data))) # b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'# 使用s.unpack() print(s.unpack(packed_data)) # (1, b'ab', 2.700000047683716) ''' 可以看到還是可以轉回來的,注意這個浮點數啊,這是計算機的存儲誤差,任何語言都是有這個問題的。 '''

  

3.字節序

import struct''' 默認地,值會使用原生C庫的字節序(endianness)來編碼。 只需在格式中提供一個顯示的字節序指令,就可以很容易地覆蓋這個默認選擇 ''' import binasciivalues = (1, "ab".encode("utf-8"), 2.7) print("original values:", values)endianness = [("@", "native, native"),("=", "native, standard"),("<", "little-endian"),(">", "big-endian"),("!", "network") ]for code, name in endianness:s = struct.Struct(code + " I 2s f")packed_data = s.pack(*values)print("*"*20)print("Format string: ", s.format, "for", name)print("uses: ", s.size, "bytes")print("hex packed data:", binascii.hexlify(packed_data))print("unpacked data", s.unpack(packed_data))# @:原生順序 # =:原生標準 # <:小端 # >:大端 # !:網絡順序''' original values: (1, b'ab', 2.7) ******************** Format string: @ I 2s f for native, native uses: 12 bytes hex packed data: b'0100000061620000cdcc2c40' unpacked data (1, b'ab', 2.700000047683716) ******************** Format string: = I 2s f for native, standard uses: 10 bytes hex packed data: b'010000006162cdcc2c40' unpacked data (1, b'ab', 2.700000047683716) ******************** Format string: < I 2s f for little-endian uses: 10 bytes hex packed data: b'010000006162cdcc2c40' unpacked data (1, b'ab', 2.700000047683716) ******************** Format string: > I 2s f for big-endian uses: 10 bytes hex packed data: b'000000016162402ccccd' unpacked data (1, b'ab', 2.700000047683716) ******************** Format string: ! I 2s f for network uses: 10 bytes hex packed data: b'000000016162402ccccd' unpacked data (1, b'ab', 2.700000047683716) '''

  

4.緩沖區

import struct''' 通常在強調性能的情況下,或者向擴展模塊傳入、傳出數據時,才會處理二進制打包數據。 通過避免為每個打包結構分配一個新緩沖區所帶來的開銷,這些情況可以得到優化。 pack_into和unpack_from方法支持直接寫入預分配的緩沖區 ''' import binascii import ctypes import arrays = struct.Struct("I 2s f") values = (1, "ab".encode("utf-8"), 2.7) print("original:", values) print("---------------") print("ctypes string buffer")# 創建一個string緩存,大小為s.size b = ctypes.create_string_buffer(s.size) print("before:", b.raw, binascii.hexlify(b.raw))# s.pack表示打包,s.pack_into表示打包到什么地方,至于第二個參數0表示偏移量,表示從頭開始 s.pack_into(b, 0, *values) print("after:", b.raw, binascii.hexlify(b.raw))# s.unpack表示解包,s.unpack_from表示從什么地方解包,參數0表示偏移量,表示從頭開始 print("unpacked:", s.unpack_from(b, 0))print("---------------") print("array")a = array.array("b", b"\0"*s.size) print("before:", a, binascii.hexlify(a)) s.pack_into(a, 0, *values) print("after:", binascii.hexlify(a)) print("unpacked:", s.unpack_from(a, 0))''' original: (1, b'ab', 2.7) --------------- ctypes string buffer before: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'000000000000000000000000' after: b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@' b'0100000061620000cdcc2c40' unpacked: (1, b'ab', 2.700000047683716) --------------- array before: array('b', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) b'000000000000000000000000' after: b'0100000061620000cdcc2c40' unpacked: (1, b'ab', 2.700000047683716) '''

  

(八)weakref:對象的弱引用

?

import weakref''' weakref支持對象的弱引用,正常的引用會增加對象的引用計數,并避免它被垃圾回收。 但結果并不是總和期望的那樣,比如有時候可能會出現一個循環引用,或者有時候需要內存時可能要刪除對象的緩存。 弱引用(weak reference)是一個不能避免對象被自動清理的對象句柄 '''

  

1.引用

import weakref''' 對象的弱引用要通過ref類來管理。要獲取原對象,可以調用引用對象 '''class RefObject:def __del__(self):print("del executed")obj = RefObject() # 創建弱引用 r = weakref.ref(obj)print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964470> # 顯示關聯RefObject print("ref:", r) # ref: <weakref at 0x000000000051BA48; to 'RefObject' at 0x0000000002964470> # 引用r加上(),等價于obj,因此得到RefObject的實例對象 print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964470>print("deleting obj") # deleting obj # 刪除obj執行析構函數 del obj # del executed # 之前說過調用r()等價于調用obj,但是obj被刪除了,所以返回None print("r():", r()) # r(): None

  

2.引用回調

import weakref''' ref構造函數可以接受一個可選的回調函數,刪除引用所指向的對象時就會調用這個回調函數 '''class RefObject:def __del__(self):print("del executed")def callback(reference):print(f"callback : {reference}")obj = RefObject() r = weakref.ref(obj, callback) ''' 當引用已經"死亡"而且不再引用原對象時,這個回調會接受這個引用對象作為參數。 這種特性的一種用法就是從緩存中刪除弱引用對象。 '''print("obj:", obj) print("ref:", r) print("ref()", r())print("deleting obj") del obj print("r():", r()) ''' obj: <__main__.RefObject object at 0x0000000002964630> ref: <weakref at 0x0000000001D2BA48; to 'RefObject' at 0x0000000002964630> ref() <__main__.RefObject object at 0x0000000002964630> deleting obj del executed callback : <weakref at 0x0000000001D2BA48; dead> # 刪除obj,執行回調,顯示dead r(): None '''

  

3.最終化對象

import weakref''' 清理若對象引用時要對資源完成更健壯的管理,可以使用finalize將回調與對象關聯。 finalize實例會一直保留(直到所關聯的對象被刪除),即使沒有保留最終化對象的引用 '''class RefObj:def __del__(self):print("xxx")def on_finalize(*args):print(f"on_finalize: {args}")obj = RefObj() weakref.finalize(obj, on_finalize, "callback的參數")del obj ''' xxx on_finalize: ('callback的參數',) ''' # finalize的參數包括要跟蹤的對象,對象被垃圾回收時要調用的callback,以及參數(可以是位置參數,也可以是關鍵字參數)# finalize實例對象還有一個atexit屬性,用來控制程序退出時是否調用這個回調(如果還未調用) obj1 = RefObj() f = weakref.finalize(obj1, on_finalize, "callback的參數") # 默認是調用回調,但是將atexit設置為False會禁用這種行為 f.atexit = False ''' 不會有任何的輸出,注意:這里我沒有顯示的刪除obj1. 因為在f.atexit=True的情況下,即使不刪除也依舊會執行callback,執行完callback會觸發析構函數 '''

  

import weakref''' 如果向finalize實例提供一個跟蹤對象的引用,這便會導致一個引用被保留,所以這個對象永遠不會被垃圾回收 '''class RefObj:def __del__(self):print("xxx")def on_finalize(*args):print(f"on_finalize: {args}")obj = RefObj() obj_id = id(obj) f = weakref.finalize(obj, on_finalize, obj) f.atexit = Falsedel obj import gc for o in gc.get_objects():if id(o) == obj_id:print("found uncollected object in gc") # found uncollected object in gc

  

4.代理

?

import weakref''' 有時候使用代理比使用弱引用更方便。使用代理可以像使用原對象一樣,而且不要求在訪問對象之前先調用代理。 這說明,可以將代理傳遞到一個庫,而這個庫并不知道它接收的是一個引用而不是真正的對象。 '''class RefObj:def __init__(self, name):self.name = namedef __del__(self):print("xxx")obj = RefObj("my obj") r = weakref.ref(obj) p = weakref.proxy(obj)print("via obj:", obj.name) # via obj: my obj print("via ref:", r().name) # via ref: my obj print("via proxy:", p.name) # via proxy: my objdel obj # xxx try:# 刪除對象之后,再調用引用,打印為Noneprint(r()) # None# 但是如果調用代理的話,則會拋出一個ReferenceErrorprint(p) except Exception as e:print(e) # weakly-referenced object no longer exists

  

(九)copy:復制對象

import copy ''' copy模塊包括兩個函數copy()和deepcopy(),用于復制現有的對象 '''

  

1.淺拷貝

import copy''' copy()創建的淺副本(shallow copy)是一個新容器,其中填充了原對象內容的引用。 ''' l1 = [1] l2 = copy.copy(l1) print(l1[0] is l2[0]) # True ''' 可以看到只是拷貝了引用 '''l2[0] = 111 print(l1) # [1] print(l2) # [111] ''' 當我們對l2進行修改的時候,由于是重新賦值,所以只會改變l2,而不會改變l1 '''# 但是,此時l1內部存放一個列表,列表是可變的對象,可以在本地修改或添加內容 l1 = [[]] l2 = copy.copy(l1) l2[0].append("xxx") print(l1) # [['xxx']] print(l2) # [['xxx']] ''' 可以看到由于只是拷貝了引用(或者說是地址,指針),因此兩者指向同一片內存。 如果是賦值,那么會指向別處。但是列表是可變類型,是支持在原處修改的,因此修改任意一個都會影響另一個 '''# 對于列表來說,l2 = l1[:],與淺拷貝是等價的 l1 = [[]] l2 = l1[:] l1[0].append(0) print(l1) # [[0]] print(l2) # [[0]]

  

2.深拷貝

import copy''' 淺拷貝則是是拷貝引用,而深拷貝則是拷貝內存里存儲的值 '''class A:def __init__(self, li):self.li = []a = A([]) a1 = copy.copy(a) a1.li.append("xxx") print(a.li) # ['xxx']# 深拷貝 a2 = copy.deepcopy(a) a2.li.remove("xxx") print(a.li) # ['xxx'] ''' 可以看到我對a2里的li,執行remove操作并沒有影響a。 '''l1 = [[]] l2 = l1[:] l2[0].append("111") print(l1) # [['111']] l3 = copy.deepcopy(l1) l3[0].append("222") print(l1) # [['111']] ''' 可以看到對l3進行操作,不會影響l1。因為深拷貝是拷貝值,所以l3操作的是自己的內存,不會影響l1 ''' print(id(l1[0])) # 31458440 print(id(l2[0])) # 31458440 print(id(l3[0])) # 31503688 ''' 所以l3[0]的引用也發生變化了,因為引用不再指向原來的內存,而是指向新的內存 '''

  

(十)pprint:美觀打印數據結構

from pprint import pprint''' pprint模塊包含一個美觀打印機,用于生成數據結構的一個美觀的視圖。格式化工具會生成數據結構的一些表示, 不僅能夠由解釋器正確的解析,還便于人閱讀。輸出會盡可能放在一行上,分解為多行時會縮進 '''

  

1.打印

from pprint import pprint from string import ascii_letters import random# 生成一堆隨機數據 d = {random.randint(1, 9): random.sample(list(ascii_letters), 3) for i in range(1, 20)} # 調用pprint函數,美觀打印??梢蕴砑右粋€stream參數,默認是打印到sys.stdout,也就是控制臺 pprint(d) ''' {1: ['O', 'W', 'F'],2: ['s', 'B', 'I'],3: ['Z', 'O', 'z'],4: ['g', 'o', 'w'],5: ['V', 'D', 'b'],6: ['l', 'z', 'q'],7: ['c', 'f', 'C'],8: ['l', 'E', 'u'],9: ['D', 'e', 'C']} ''' # 相比打印在一行,這樣是不是很美觀呢?

  

2.格式化

from pprint import pformat from string import ascii_letters import random''' 如果我們想美觀地把數據寫到某一個地方中,該怎么辦呢? 我們使用pprint可以美觀地打印到控制臺當中,但我如果不想打印,而是存到某一個地方,或者賦值給某個變量呢? 可以使用pformat,美觀格式化 '''# 生成一堆隨機數據 d = {random.randint(1, 9): random.sample(list(ascii_letters), 3) for i in range(1, 20)}p = pformat(d) print(p) ''' {1: ['g', 'R', 'a'],2: ['O', 'k', 'M'],3: ['y', 'u', 'I'],4: ['V', 'E', 'P'],5: ['c', 'e', 'h'],6: ['B', 'i', 'U'],7: ['w', 'z', 'K'],8: ['x', 'z', 'q'],9: ['I', 'B', 'W']} '''

  

?

轉載于:https://www.cnblogs.com/traditional/p/10444299.html

總結

以上是生活随笔為你收集整理的3.Python3标准库--数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。