流暢的python中有不少奇技淫巧,整本書都在強調如何最大限度地利用Python 標準庫。介紹了不少python的不經常使用的數據類型、操做、庫等,對於入門python後想要提高對python的認識應該有幫助。目前讀一遍記錄了一些有共鳴的操做:html
按可存放的元素類型分爲:容器序列和扁平序列python
常見的容器序列包括:list,tuple,array.array,collections.deque等。程序員
常見的扁平序列包括:str,bytes,bytearray, memoryview, array.array等。express
按序列可否被修改分爲:可變序列與不可變序列vim
標準庫裏collections模塊中提供了不少與字典類型類似的變種。segmentfault
OrderDict: 這個類型在添加鍵的時候,會保存順序,所以鍵的迭代順序老是一致的數組
ChainMap: 該類型能夠容納數個不一樣的映射對像,在進行鍵的查找時,這些對象會被當作一個總體逐個查找,直到鍵被找到爲止安全
Counter: 這個映射類型會給鍵準備一個整數技術器,每次更行一個鍵的時候都會增長這個計數器,因此這個類型能夠用來給散列表對象計數,或者當成多重集來用。多線程
UserDict: 這個類其實就是把標準的dict用Python又寫了一遍。通常用來給程序員想要經過繼承dict建立本身的dict時,代替dict使用的。主要是由於直接繼承原生dict會出現bug。app
defaultdict:處理找不到的鍵的一個選擇
當某個鍵不在映射裏, 咱們也但願也能獲得一個默認值. 這就是 defaultdict , 它是 dict 的子類, 並實現了 missing 方法.
鍵必須是可散列的: 一個可散列的對象必須知足如下要求。 (1) 支持 hash() 函數,而且經過 __hash__() 方法所獲得的散列值是不變的。 (2) 支持經過 __eq__() 方法來檢測相等性。 (3) 若 a == b 爲真,則 hash(a) == hash(b) 也爲真。 全部由用戶自定義的對象默認都是可散列的,由於它們的散列值由 id() 來獲取,而 且它們都是不相等的。 字典在內存上開銷很大(用內存換效率)。 元組取代字典就能節省空間的緣由有兩個: (1) 避免了散列表所耗費的空間, (2) 無需把記錄中字段的名字在每一個元素裏都存一遍。 鍵的查詢很快 鍵的次序取決於添加順序 往字典裏添加新鍵可能會改變已有鍵的順序
結合的元素必須是可散列的 集合和消耗內存 能夠很高效的判斷元素是否存在於某個集合 元素的次序取決於被添加到集合裏的順序 往集合裏添加元素,可能會改變集合裏已有的元素次序
建立一個具名元組須要兩個參數,一個是類名,另外一個是類的各個字段的名字。後者
能夠是由數個字符串組成的可迭代對象,或者是由空格分隔開的字段名組成的字符串。
>>> from collections import namedtuple >>> City = namedtuple('City', 'name country population coordinates') >>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) >>> tokyo City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667)) >>> tokyo.population 36.933 >>> tokyo.coordinates (35.689722, 139.691667) >>> tokyo[1] 'JP' >>> City = namedtuple('City_Name', 'name country population coordinates') >>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) >>> tokyo City_Name(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
有跟可變序列有關的操做,包括 .pop、.insert 和 .extend。另外,數組還提供從文件
讀取和存入文件的更快的方法,如 .frombytes 和 .tofile。
片。
在進行格式化輸出時,%r 與 %s 的區別就比如 repr() 函數處理對象與 str() 函數處理對象的差異。
本文重點列舉一些兩者的差別化用法:
>> s = 'world' >> print('hello %s'%s) hello world >> print('hello %r'%s) hello 'world' >> str(s) 'world' >> repr(s) "'world'" 2. datetime 庫中的 datetime 對象 >> from datetime import datetime >> timeinfo = datetime.today() >> timeinfo datetime.datetime(2016, 6, 7, 21, 17, 34, 925488) >> type(timeinfo) datetime.datetime >> repr(timeinfo) 'datetime.datetime(2016, 6, 7, 21, 17, 34, 925488)' >> str(timeinfo) '2016-06-07 21:17:34.925488'
Python dis 模塊支持對Python代碼進行反彙編, 生成字節碼指令。
In[1]: def test(): ... x = 1 ... if x < 3: ... return "yes" ... else: ... return "no" In[2]: dis.dis(test) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (x) 3 6 LOAD_FAST 0 (x) 9 LOAD_CONST 2 (3) 12 COMPARE_OP 0 (<) 15 POP_JUMP_IF_FALSE 22 4 18 LOAD_CONST 3 ('yes') 21 RETURN_VALUE 6 >> 22 LOAD_CONST 4 ('no') 25 RETURN_VALUE 26 LOAD_CONST 0 (None) 29 RETURN_VALUE >>> def add(a, b = 0): ... return a + b ... >>> >>> dis.dis(add) 2 0 LOAD_FAST 0 (a) 2 LOAD_FAST 1 (b) 4 BINARY_ADD 6 RETURN_VALUE >>>
class memoryview(obj)是python的內置類,若是要用memoryview 去引用一個object, 那麼這個object 必須支持buffer protocol, python3 中原生(built-in) 支持buffer protocol的obj有bytes和bytearray,memoryview可使用不一樣的方式讀取和操做同一塊內存,而且原有的內存字節不會隨意移動。相似於C中的強轉,好處是不會有內存拷貝。
例如,使用memoryview修改一個短整型有符號整數數組的數據。
from array import array from random import random numbers = array('h', [-2, -1, 0, 1, 2]) #signed short memv = memoryview(numbers) #5個短整型有符號整數的數組建立一個memoryview print (len(memv)) #打印長度 print (memv.tolist()) #轉換成列表形式 memv_oct = memv.cast('B') #內存共享 轉換成無符號字符類型 print (memv_oct.tolist()) memv_oct[5] = 4 #把位置5的字節賦值成4 print (numbers) #由於咱們把佔 2 個字節的整數的高位字節改爲了 4,因此這個有符號整數的值就變成了 1024 輸出以下: 5 #數組長度 [-2, -1, 0, 1, 2] #列表形式顯示 [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]#長度擴大一倍 轉換爲無符號字符類型 array('h', [-2, -1, 1024, 1, 2]) #原來的數組被修改
bytearray是可變(mutable)的字節序列,相對於Python2中的str,但str是不可變(immutable)的。
在Python3中因爲str默認是unicode編碼,因此只有經過bytearray才能按字節訪問。
下面兩種行爲的對比:
簡單點就是,str和bytearray的切片操做會產生新的切片str和bytearry並拷貝數據,使用memoryview以後不會。
python2中的例子
>> a = 'aaaaaa' >> b = a[:2] # 會產生新的字符串 >> a = bytearray('aaaaaa') >> b = a[:2] # 會產生新的bytearray >> b[:2] = 'bb' # 對b的改動不影響a >> a bytearray(b'aaaaaa') >> b bytearray(b'bb')
>> a = 'aaaaaa' >> ma = memoryview(a) >> ma.readonly # 只讀的memoryview True >> mb = ma[:2] # 不會產生新的字符串 >> a = bytearray('aaaaaa') >> ma = memoryview(a) >> ma.readonly # 可寫的memoryview False >> mb = ma[:2] # 不會會產生新的bytearray >> mb[:2] = 'bb' # 對mb的改動就是對ma的改動 >> mb.tobytes() 'bb' >> ma.tobytes() 'bbaaaa'
Python 中有各類各樣可調用的類型,所以判斷置的 callable() 函數:
>>> abs, str, 13 (<built-in function abs>, <class 'str'>, 13) >>> [callable(obj) for obj in (abs, str, 13)] [True, True, False]
>>> import random >>> a=range(10) >>> random.shuffle(a) >>> a [1, 0, 8, 5, 6, 7, 9, 3, 2, 4] >>> random.shuffle(a) >>> a [7, 5, 6, 2, 1, 8, 9, 0, 3, 4]
Math Function Description abs() Returns absolute value of a number divmod() Returns quotient and remainder of integer division max() Returns the largest of the given arguments or items in an iterable min() Returns the smallest of the given arguments or items in an iterable pow() Raises a number to a power round() Rounds a floating-point value sum() Sums the items of an iterable Type Conversion Function Description ascii() Returns a string containing a printable representation of an object bin() Converts an integer to a binary string bool() Converts an argument to a Boolean value chr() Returns string representation of character given by integer argument complex() Returns a complex number constructed from arguments float() Returns a floating-point object constructed from a number or string hex() Converts an integer to a hexadecimal string int() Returns an integer object constructed from a number or string oct() Converts an integer to an octal string ord() Returns integer representation of a character repr() Returns a string containing a printable representation of an object str() Returns a string version of an object type() Returns the type of an object or creates a new type object Iterables and Iterators Function Description all() Returns True if all elements of an iterable are true any() Returns True if any elements of an iterable are true enumerate() Returns a list of tuples containing indices and values from an iterable filter() Filters elements from an iterable iter() Returns an iterator object len() Returns the length of an object map() Applies a function to every item of an iterable next() Retrieves the next item from an iterator range() Generates a range of integer values reversed() Returns a reverse iterator slice() Returns a slice object sorted() Returns a sorted list from an iterable zip() Creates an iterator that aggregates elements from iterables Composite Data Type Function Description bytearray() Creates and returns an object of the bytearray class bytes() Creates and returns a bytes object (similar to bytearray, but immutable) dict() Creates a dict object frozenset() Creates a frozenset object list() Constructs a list object object() Returns a new featureless object set() Creates a set object tuple() Creates a tuple object Classes, Attributes, and Inheritance Function Description classmethod() Returns a class method for a function delattr() Deletes an attribute from an object getattr() Returns the value of a named attribute of an object hasattr() Returns True if an object has a given attribute isinstance() Determines whether an object is an instance of a given class issubclass() Determines whether a class is a subclass of a given class property() Returns a property value of a class setattr() Sets the value of a named attribute of an object super() Returns a proxy object that delegates method calls to a parent or sibling class Input/Output Function Description format() Converts a value to a formatted representation input() Reads input from the console open() Opens a file and returns a file object print() Prints to a text stream or the console Variables, References, and Scope Function Description dir() Returns a list of names in current local scope or a list of object attributes globals() Returns a dictionary representing the current global symbol table id() Returns the identity of an object locals() Updates and returns a dictionary representing current local symbol table vars() Returns __dict__ attribute for a module, class, or object Miscellaneous Function Description callable() Returns True if object appears callable compile() Compiles source into a code or AST object eval() Evaluates a Python expression exec() Implements dynamic execution of Python code hash() Returns the hash value of an object help() Invokes the built-in help system memoryview() Returns a memory view object staticmethod() Returns a static method for a function __import__() Invoked by the import statement
類別 方法名 字符串 / 字節序列表示形式 __repr__、__str__、__format__、__bytes__ 數值轉換 __abs__、__bool__、__complex__、__int__、__float__、__hash__、__index__ 集合模擬 __len__、__getitem__、__setitem__、__delitem__、__contains__ 迭代枚舉 __iter__、__reversed__、__next__ 可調用模擬 __call__ 上下文管理 __enter__、__exit__ 實例建立和銷燬 __new__、__init__、__del__ 屬性管理 __getattr__、__getattribute__、__setattr__、__delattr__、__dir__ 屬性描述符 __get__、__set__、__delete__ 跟類相關的服務 __prepare__、__instancecheck__、__subclasscheck__
bisect.bisect_left(a,x, lo=0, hi=len(a)) : 查找在有序列表 a 中插入 x 的index。lo 和 hi 用於指定列表的區間,默認是使用整個列表。若是 x 已經存在,在其左邊插入。返回值爲 index。 bisect.bisect_right(a,x, lo=0, hi=len(a)) bisect.bisect(a, x,lo=0, hi=len(a)) : 這2個函數和 bisect_left 相似,但若是 x 已經存在,在其右邊插入。 bisect.insort_left(a,x, lo=0, hi=len(a)) : 在有序列表 a 中插入 x。和 a.insert(bisect.bisect_left(a,x, lo, hi), x) 的效果相同。 bisect.insort_right(a,x, lo=0, hi=len(a)) bisect.insort(a, x,lo=0, hi=len(a)) : 和 insort_left 相似,但若是 x 已經存在,在其右邊插入。 Bisect 模塊提供的函數能夠分兩類: bisect* 只用於查找 index, 不進行實際的插入;而 insort* 則用於實際插入。
from array import array from random import random floats = array('d', (random() for i in range(10**7))) fp = open('floats.bin', 'wb') floats.tofile(fp) fp.close() floats2 = array('d') fp = open('floats.bin', 'rb') floats2.fromfile(fp, 10**7) fp.close() floats2 == floats
from queue import Queue #LILO隊列 q = Queue() #建立隊列對象 q.put(0) #在隊列尾部插入元素 q.put(1) q.put(2) print('LILO隊列',q.queue) #查看隊列中的全部元素 print(q.get()) #返回並刪除隊列頭部元素 print(q.queue) from queue import LifoQueue #LIFO隊列 lifoQueue = LifoQueue() lifoQueue.put(1) lifoQueue.put(2) lifoQueue.put(3) print('LIFO隊列',lifoQueue.queue) lifoQueue.get() #返回並刪除隊列尾部元素 lifoQueue.get() print(lifoQueue.queue) from queue import PriorityQueue #優先隊列 priorityQueue = PriorityQueue() #建立優先隊列對象 priorityQueue.put(3) #插入元素 priorityQueue.put(78) #插入元素 priorityQueue.put(100) #插入元素 print(priorityQueue.queue) #查看優先級隊列中的全部元素 priorityQueue.put(1) #插入元素 priorityQueue.put(2) #插入元素 print('優先級隊列:',priorityQueue.queue) #查看優先級隊列中的全部元素 priorityQueue.get() #返回並刪除優先級最低的元素 print('刪除後剩餘元素',priorityQueue.queue) priorityQueue.get() #返回並刪除優先級最低的元素 print('刪除後剩餘元素',priorityQueue.queue) #刪除後剩餘元素 priorityQueue.get() #返回並刪除優先級最低的元素 print('刪除後剩餘元素',priorityQueue.queue) #刪除後剩餘元素 priorityQueue.get() #返回並刪除優先級最低的元素 print('刪除後剩餘元素',priorityQueue.queue) #刪除後剩餘元素 priorityQueue.get() #返回並刪除優先級最低的元素 print('所有被刪除後:',priorityQueue.queue) #查看優先級隊列中的全部元素 from collections import deque #雙端隊列 dequeQueue = deque(['Eric','John','Smith']) print(dequeQueue) dequeQueue.append('Tom') #在右側插入新元素 dequeQueue.appendleft('Terry') #在左側插入新元素 print(dequeQueue) dequeQueue.rotate(2) #循環右移2次 print('循環右移2次後的隊列',dequeQueue) dequeQueue.popleft() #返回並刪除隊列最左端元素 print('刪除最左端元素後的隊列:',dequeQueue) dequeQueue.pop() #返回並刪除隊列最右端元素 print('刪除最右端元素後的隊列:',dequeQueue) 以上隊列在多線程中可使用的且線程安全,但在多進程中都不能用於通訊。在多進程中,須要這樣使用: from multiprocessing import Process, Queue myqueue = Queue(100) ## 參考 https://blog.csdn.net/sinat_38682860/article/details/80392493 https://www.cnblogs.com/cmnz/p/6936181.html
from keyword import kwlist print(kwlist)
import builtins dir(builtins)
https://segmentfault.com/a/1190000012724861 def test(): globals()['a2'] = 4 test() print a2 # 輸出 4 def aaaa(): print locals() for i in ['a', 'b', 'c']: locals()[i] = 1 print locals() print a # 錯誤:NameError: global name 'a' is not defined aaaa()
動態地進行變量賦值時,locals() 看到的, 的確是函數的局部命名空間的內容, 可是它自己不能表明局部命名空間, 這就好像一個代理, 它收集了A, B, C的東西, 展現給我看, 可是我卻不能簡單的經過改變這個代理, 來改變A, B, C真正擁有的東西!
這也就是爲何, 當咱們經過locals()[i] = 1的方式去動態賦值時, print a卻觸發了NameError異常, 而相反的, globals()確實真正的全局命名空間,
因此通常會說locals() 只讀, globals() 可讀可寫。
對於通常不可變類型的變量來講這兩個方法沒啥區別,但對於可變類型如list(列表),dict(字典)就有區別了,x += y 就地改變了list的值,而x = x + y建立了一個新的list並從新將x綁定上去,經過id(x)就能夠看出。
l = l + [3, 4, 5] 這種背後就是BINARY_ADD l += [3, 4, 5] 這種背後就是INPLACE_ADD
+=實際上應該能算是一個增強版的+, 由於它比+多了一個寫回自己的功能.不過是否可以寫回自己, 仍是得看對象自身是否支持, 也就是說是否具有Py_NotImplemented標識, 是否支持sq_inplace_concat, 若是具有, 才能實現, 不然, 也就是和 + 效果同樣而已.
不只僅是這些,當混合使用可變類型和不可變類型的時候,你會有更加驚奇的發現:
>>> t = ([],) >>> t[0] += [2, 3] Traceback (most recent call last): File "<input>", line 1, in ? TypeError: object doesn't support item assignment >>> t ([2, 3],)
明顯的,元組不支持對其中元素的賦值——可是在對他使用+=後,元組裏的list確實改變了!緣由依然是+=就地改變list的值。可是元組的賦值不被容許,當異發生時,元組中的list已經被就地改變了。
這就是一個我我的以爲很是致命的陷阱。
解決方法:乾脆避免使用+=,或者僅僅在整數時使用它。
>>> a=(1) # 錯誤姿式 >>> type(a) <type 'int'> >>> a=(1,) # 正確姿式 >>> type(a) <type 'tuple'>
通用的解決方案:
num_list = [1, 2, 3, 4, 5, 2, 2, 4] 1. 倒序循環遍歷 for i in range(len(num_list) - 1, -1, -1): # 講究 if num_list[i] == 2: num_list.pop(i) print(num_list) 2. 遍歷拷貝的list,操做原始的list。對於過大的list,拷貝後可能很佔內存,能夠用倒序遍歷的方法來實現。 for item in num_list[:]: # 保證能夠把num_list從頭遍歷到尾 if item == 2: num_list.remove(item) # 從頭刪除遇到的第一個item print(num_list) 3. 對原來的列表作過濾,生成一個新的列表(假設determine(x)爲判斷條件的函數): list = [x for x in list if not determine(x)] 4. 在原來列表上作切片,僅保留須要的元素 list[:] = [x for x in list if not determine(x)] 5. python2.x ifilterfalse()方法 from itertools import ifilterfalse() list[:] = ifilterfalse(determine, list) 6. Python3 filterfalse()方法 from itertools import filterfalse list[:] = filterfalse(determine, list) 方法5,6對列表的修改會反應到其餘對此列表的引用上。
def local_var_err(): b += [3] # UnboundLocalError: local variable 'b' referenced before assignment b = b + [2] # UnboundLocalError: local variable 'b' referenced before assignment
In [109]: %timeit -n100 a = (i for i in range(100000)) 100 loops, best of 3: 659 µs per loop In [110]: %timeit -n100 b = [i for i in range(100000)] 100 loops, best of 3: 2.68 ms per loop
In [112]: %timeit -n100 for x in (i for i in range(100000)):pass 100 loops, best of 3: 4.23 ms per loop In [113]: %timeit -n100 for x in [i for i in range(100000)]:pass 100 loops, best of 3: 3.49 ms per loop
空間換時間
# -*- coding:utf-8 -*- import timeit test_dict = {} class dome(object): def test_class(self): num = 100 self.test_dict = {} # 爲了公平,每次執行都一樣初始化新的 {} for i in range(num): self.test_dict[i] = i def test_local(self): num = 100 test_dict = {} # 爲了公平,每次執行都一樣初始化新的 {} for i in range(num): test_dict[i] = i self.test_dict = test_dict def test_global(self): num = 100 global test_dict test_dict = {} # 爲了公平,每次執行都一樣初始化新的 {} for i in range(num): test_dict[i] = i s = dome() print(timeit.timeit(stmt=s.test_class)) # 9.75976037823 print(timeit.timeit(stmt=s.test_local)) # 7.17526431985 print(timeit.timeit(stmt=s.test_global)) # 7.57540534177 """ 1. 訪問局部變量速度要快不少 2. 循環以外能作的事不要放在循環內 在一些會頻繁操做 類/實例屬性 的狀況下,應該是先把 屬性 取出來存到 局部變量,而後用 局部變量 來完成操做。最後視狀況把變更更新到 屬性 上。 """
a=list(str(range(1000))) In [126]: %%timeit s="" for x in a: s+=x .....: 1000 loops, best of 3: 304 µs per loop In [127]: %%timeit .....: s="".join(a) .....: 10000 loops, best of 3: 59.3 µs per loop 參考博客 https://blog.csdn.net/xdhstc/article/details/51719892
# -*- coding:utf-8 -*- import timeit def test_1(): a = [True] * 100 s = [] for i in a: if i == True: s.append(i) return s def test_2(): a = [True] * 100 return [i for i in a if i is True] def test_3(): a = [True] * 100 return [i for i in a if i == True] def test_4(): a = [True] * 100 return [i for i in a if i] print(timeit.timeit(stmt=test_1)) # 11.5888194259 print(timeit.timeit(stmt=test_2)) # 6.00562291202 print(timeit.timeit(stmt=test_3)) # 7.15504198257 print(timeit.timeit(stmt=test_4)) # 4.29275713242
In [145]: %timeit -n100000 c = pow(2,20) 100000 loops, best of 3: 89.3 ns per loop In [146]: %timeit -n100000 c = 2**20 100000 loops, best of 3: 22.2 ns per loop
# -*- coding:utf-8 -*- import timeit def test1(): s = [] for z in range(10): for y in range(100): for x in range(1000): if x > 100 and y > 50 and z > 5: return s s.append((x, y, z)) def test2(): s = [] for x in range(1000): for y in range(100): for z in range(10): if x > 100 and y > 50 and z > 5: return s s.append((x, y, z)) print(timeit.timeit(stmt=test1, number=100)) # 14.1777687741 print(timeit.timeit(stmt=test2, number=100)) # 2.03417086749 print(sorted(test1()) == sorted(test2())) # False print(len(test1())) # 651101 print(len(test2())) # 101516