3.Heapq
heapq模塊使用一個用堆實現的優先級隊列。堆是一種簡單的有序列表,并且置入了堆的相關規則。
堆是一種樹形的數據結構,樹上的子節點與父節點之間存在順序關系。二叉堆(binary heap)能夠用一個經過組織的列表或數組結構來標識,在這種結構中,元素N的子節點的序號為2*N+1和2*N+2(下標始于0)。簡單來說,這個模塊中的所有函數都假設序列是有序的,所以序列中的第一個元素(seq[0])是最小的,序列的其他部分構成一個二叉樹,并且seq[i]節點的子節點分別為seq[2*i+1]以及seq[2*i+2]。當對序列進行修改時,相關函數總是確保子節點大于等于父節點。
import heapq
heap = []
for value in [20, 10, 30, 50, 40]:
heapq.heappush(heap, value)
while heap:
print heapq.heappop(heap)
heapq模塊有兩個函數nlargest()和nsmallest(),顧名思義,讓我們來看看它們的用法。
import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]
兩個函數也能夠通過一個鍵參數使用更為復雜的數據結構,例如:
import heapq
portfolio = [
{'name': 'IBM', 'shares': 100, 'price': 91.1},
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
{'name': 'FB', 'shares': 200, 'price': 21.09},
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
{'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
print cheap
# [{'price': 16.35, 'name': 'YHOO', 'shares': 45},
# {'price': 21.09, 'name': 'FB', 'shares': 200}, {'price': 31.75, 'name': 'HPQ', 'shares': 35}]
print expensive
# [{'price': 543.22, 'name': 'AAPL', 'shares': 50}, {'price': 115.65, 'name': 'ACME',
# 'shares': 75}, {'price': 91.1, 'name': 'IBM', 'shares': 100}]
來看看如何實現一個根據給定優先級進行排序,并且每次pop操作都返回優先級最高的元素的隊列例子。
import heapq
class Item:
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Item({!r})'.format(self.name)
class PriorityQueue:
def __init__(self):
self._queue = []
self._index = 0
def push(self, item, priority):
heapq.heappush(self._queue, (-priority, self._index, item))
self._index += 1
def pop(self):
return heapq.heappop(self._queue)[-1]
q = PriorityQueue()
q.push(Item('foo'), 1)
q.push(Item('bar'), 5)
q.push(Item('spam'), 4)
q.push(Item('grok'), 1)
print q.pop() # Item('bar')
print q.pop() # Item('spam')
print q.pop() # Item('foo')
print q.pop() # Item('grok')
4. Bisect
bisect模塊能夠提供保持list元素序列的支持。它使用了二分法完成大部分的工作。它在向一個list插入元素的同時維持list是有序的。在某些情況下,這比重復的對一個list進行排序更為高效,并且對于一個較大的list來說,對每步操作維持其有序也比對其排序要高效。
假設你有一個range集合:
a = [(0, 100), (150, 220), (500, 1000)]
如果我想添加一個range (250, 400),我可能會這么做:
import bisect
a = [(0, 100), (150, 220), (500, 1000)]
bisect.insort_right(a, (250,400))
print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]
我們可以使用bisect()函數來尋找插入點:
import bisect
a = [(0, 100), (150, 220), (500, 1000)]
bisect.insort_right(a, (250,400))
bisect.insort_right(a, (399, 450))
print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]
print bisect.bisect(a, (550, 1200)) # 5
bisect(sequence, item) => index 返回元素應該的插入點,但序列并不被修改。
import bisect
a = [(0, 100), (150, 220), (500, 1000)]
bisect.insort_right(a, (250,400))
bisect.insort_right(a, (399, 450))
print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]
print bisect.bisect(a, (550, 1200)) # 5
bisect.insort_right(a, (550, 1200))
print a # [(0, 100), (150, 220), (250, 400), (399, 450), (500, 1000), (550, 1200)]
新元素被插入到第5的位置。
5. Weakref
weakref模塊能夠幫助我們創建Python引用,卻不會阻止對象的銷毀操作。這一節包含了weak reference的基本用法,并且引入一個代理類。
在開始之前,我們需要明白什么是strong reference。strong reference是一個對對象的引用次數、生命周期以及銷毀時機產生影響的指針。strong reference如你所見,就是當你將一個對象賦值給一個變量的時候產生的:
>>> a = [1,2,3]
>>> b = a
在這種情況下,這個列表有兩個strong reference,分別是a和b。在這兩個引用都被釋放之前,這個list不會被銷毀。
class Foo(object):
def __init__(self):
self.obj = None
print 'created'
def __del__(self):
print 'destroyed'
def show(self):
print self.obj
def store(self, obj):
self.obj = obj
a = Foo() # created
b = a
del a
del b # destroyed
Weak reference則是對對象的引用計數器不會產生影響。當一個對象存在weak reference時,并不會影響對象的撤銷。這就說,如果一個對象僅剩下weak reference,那么它將會被銷毀。
你可以使用weakref.ref函數來創建對象的weak reference。這個函數調用需要將一個strong reference作為第一個參數傳給函數,并且返回一個weak reference。
>>> import weakref
>>> a = Foo()
created
>>> b = weakref.ref(a)
>>> b
一個臨時的strong reference可以從weak reference中創建,即是下例中的b():
>>> a == b()
True
>>> b().show()
None
請注意當我們刪除strong reference的時候,對象將立即被銷毀。
>>> del a
destroyed
如果試圖在對象被摧毀之后通過weak reference使用對象,則會返回None:
>>> b() is None
True
若是使用weakref.proxy,就能提供相對于weakref.ref更透明的可選操作。同樣是使用一個strong reference作為第一個參數并且返回一個weak reference,proxy更像是一個strong reference,但當對象不存在時會拋出異常。
>>> a = Foo()
created
>>> b = weakref.proxy(a)
>>> b.store('fish')
>>> b.show()
fish
>>> del a
destroyed
>>> b.show()
Traceback (most recent call last):
File "", line 1, in ?
ReferenceError: weakly-referenced object no longer exists
完整的例子:
引用計數器是由Python的垃圾回收器使用的,當一個對象的應用計數器變為0,則其將會被垃圾回收器回收。
最好將weak reference用于開銷較大的對象,或避免循環引用(雖然垃圾回收器經常干這種事情)。
import weakref
import gc
class MyObject(object):
def my_method(self):
print 'my_method was called!'
obj = MyObject()
r = weakref.ref(obj)
gc.collect()
assert r() is obj #r() allows you to access the object referenced: it's there.
obj = 1 #Let's change what obj references to
gc.collect()
assert r() is None #There is no object left: it was gc'ed.
提示:只有library模塊中定義的class instances、functions、methods、sets、frozen sets、files、generators、type objects和certain object types(例如sockets、arrays和regular expression patterns)支持weakref。內建函數以及大部分內建類型如lists、dictionaries、strings和numbers則不支持。