fluent_python1

Magic Method

python中有些跟對象自己有關的方法, 以兩個下劃線開始,兩個下劃線結束, 通常稱爲魔法方法(magic method). 好比 obj[key] 的背後就是 __getitem__ 方法,爲了能求得my_collection[key] 的值,解釋器實際上會調用my_collection.__getitem__(key)python

首先明確一點,特殊方法的存在是爲了被 Python 解釋器調用的,你本身並不須要調用它們.git

特殊幾個方法:算法

__init__() 構造函數
__len__() 返回集合的Count
__getitem__() 根據傳入的位置參數,取出集合內的對象

__repr__() 根據對象的屬性等返回一個字符串表示
    __repr__ 和 __str__ 的區別在於,後者是在 str() 函數被使用,或是在用 print 函數打印一個對象的時候才被調用的,而且它返回的字符串對終端用戶更友好。
    若是你只想實現這兩個特殊方法中的一個,__repr__ 是更好的選擇,由於若是一個對象沒有 __str__ 函數,而 Python 又須要調用它的時候,解釋器會用 __repr__ 做爲替代

__add__ 和 __mul__ 分別實現了簡單的+和*的運算符重載, 中綴運算符的基本原則就是不改變操做對象,而是產出一個新的值.

__bool__ bool(x) 的背後是調用 x.__bool__() 的結果;若是不存在 __bool__ 方法,那麼 bool(x) 會嘗試調用 x.__len__()。若返回 0,則 bool 會返回False;不然返回 True。

其他的一些魔法方法能夠在Python文檔中搜索Data Model查看.

len()爲何不是普通函數, 若是是普通函數據就直接從實例對象中取出該函數或者是對應的屬性值就能夠了, 可是若是是外在的實現len()就能夠把len()適用於自定義函數, 而且具備一樣的風格而不是有時候是count()有時候是len()數組

list like type

容器序列數據結構

  • list、tuple 和 collections.deque 這些序列能存放不一樣類型的數據。

扁平序列多線程

  • str、bytes、bytearray、memoryview 和 array.array,這類序列只能容納一種類型。

因此容器類型是存儲的不一樣類型的對象的引用, 扁平類型是直接在一塊連續內存中存儲的值, 注意不是引用.可是裏面只能放字符, 字節, 數值等這種基本類型.app

可變序列ide

  • list、bytearray、array.array、collections.deque 和 memoryview。

不可變序列函數

  • tuple、str 和 bytes。

列表推導和生成器表達式
列表推導是構建列表(list)的快捷方式,而生成器表達式則能夠用來建立其餘任何類型的序列.ui

>>> symbols = '$¢£¥€¤'
>>> codes = []
>>> for symbol in symbols:
...     codes.append(ord(symbol))
...
>>> codes
[36, 162, 163, 165, 8364, 164]

沒使用列表推導

>>> symbols = '$¢£¥€¤'
>>> codes = [ord(symbol) for symbol in symbols]
>>> codes
[36, 162, 163, 165, 8364, 164]

使用列表推導, 通常來講不要超過兩行

列表推導、生成器表達式,以及同它們很類似的集合(set)推導和字典(dict)推導,在 Python 3 中都有了本身的局部做用域,就像函數似的。表達式內部的變量和賦值只在局部起做用,表達式的上下文裏的同名變量還能夠被正常引用,局部變量並不會影響到它們。

>>> x = 'ABC'
>>> dummy = [ord(x) for x in x]
>>> x ➊
'ABC'
>>> dummy ➋
[65, 66, 67]
>>>

在python2.x中可能會出現x=C的狀況, 即局部變量會覆蓋外部變量, 沒有本身的做用域, 在Python3中沒有這種問題.

>>> symbols = '$¢£¥€¤'
>>> beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
>>> beyond_ascii
[162, 163, 165, 8364, 164]
>>> beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
>>> beyond_ascii
[162, 163, 165, 8364, 164]

列表推導和filter和map的對比

列表推導一個很重要的使用場景就是生成笛卡爾集,

self._cards = [Card(rank, suit) for rank in self.ranks
                                for suit in self.suits]

如上, 這樣的代碼比較多, 要想改變生成的元組的構成, 只要改變兩個for的順序便可, 固然也是至關於兩層循環.

列表推導的做用只有一個:生成列表。若是想生成其餘類型的序列,生成器表達式就派上了用場。

>>> colors = ['black', 'white']
>>> sizes = ['S', 'M', 'L']
>>> for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes): ➊
...     print(tshirt)
...
black S
black M
black L
white S
white M
white L

和列表推導不一樣, 生成器表達式是在循環真正執行的時候纔會每次生成一個對象出來, 而不會先生成一個列表, 而後往裏面填充, 更節省內存.

tuple like

元組實際上是對數據的記錄:元組中的每一個元素都存放了記錄中一個字段的數據,外加這個字段的位置。正是這個位置信息給數據賦予了意義。

元組更相似一種數據輸入輸出的格式, 在對tuple作某些運算的時候能夠保持聚合.
元組拆包能夠應用到任何可迭代對象上,惟一的硬性要求是,被可迭代對象中的元素數量必需要跟接受這些元素的元組的空檔數一致。不然則必須用*表示省略.
通常的拆包的例子以下:

>>> lax_coordinates = (33.9425, -118.408056)
>>> latitude, longitude = lax_coordinates # 元組拆包
>>> latitude
33.9425
>>> longitude
-118.408056

因此函數返回多個值其實也是返回一個tuple, 後面再進行拆包, 若是對不感興趣的字段用"_"表示.
對於拆包中也能夠有多個參數省略的作法, 使用*. 如:

>>> a, *body, c, d = range(5)
>>> a, body, c, d
(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)
>>> head, b, c, d
([0, 1], 2, 3, 4)

注意這裏的*必須放在一個變量前, 而變量也會變成一個list. 並且這裏的拆包和結構還能夠嵌套使用, 只要等式右邊的結構是同構的.

collections.namedtuple()

相比tuple多了字段名, 可是對於通常的類實例, 也少了不少存儲的內容, 本質上是把字段名做爲類屬性存儲, 另外的值存儲在tuple中.

建立一個具名元組須要兩個參數,一個是類名,另外一個是類的各個字段的名字。後者能夠是由數個字符串組成的可迭代對象,或者是由空格分隔開的字段名組成的字符串.

>>> City._fields ➊
('name', 'country', 'population', 'coordinates')
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data) ➋
>>> delhi._asdict() ➌
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population',
21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
>>> for key, value in delhi._asdict().items():
        print(key + ':', value)
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)
>>>

❶ _fields 屬性是一個包含這個類全部字段名稱的元組。
❷ 用 _make() 經過接受一個可迭代對象來生成這個類的一個實例,它的做用跟
City(*delhi_data) 是同樣的。
❸ _asdict() 把具名元組以 collections.OrderedDict 的形式返回,咱們能夠利用它
來把元組裏的信息友好地呈現出來。

切片高級

切片和C語言中同樣, 都是忽略最後一個元素, 而且使用0開頭, 這樣的話好處有二:

  1. 能夠根據任何一個下標來進行對集合的分割
  2. 任意兩個下標的差就是相差的元素數

切片能夠支持步長, seq.__getitem__(slice(start, stop, step)), 且步長能夠爲負.
切片也支持賦值和del, 以下:

>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[2:5] = [20, 30]
>>> l
[0, 1, 20, 30, 5, 6, 7, 8, 9]
>>> del l[5:7]
>>> l
[0, 1, 20, 30, 5, 8, 9]
>>> l[3::2] = [11, 22]
>>> l
[0, 1, 20, 11, 5, 22, 9]
>>> l[2:5] = 100 ➊
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
>>> l[2:5] = [100]
>>> l
[0, 1, 100, 22, 9]

這裏要注意, 右側的對象必須是一個可迭代的對象, 即便只有一個也要變成可迭代的.

對序列使用+和*, 都是不改變原序列, 從新建一個序列, 而後填充.
當序列中具備可變對象的引用時, 實際上是一個對象的多個拷貝, 並非新建的對象拷貝.

當對可變序列進行+=和*=操做時, 若是實現了_iadd_(), 那麼就是原地增長, 不然則是尋找_add_(), 會從新構造一個序列而後拷貝過去.
當對不可變序列, 如tuple進行操做時, 就是直接拷貝增長了.

注意

  • 不要把可變對象放在元組裏面。
  • 增量賦值不是一個原子操做。咱們剛纔也看到了,它雖然拋出了異常,但仍是完成了操做。
  • 查看 Python 的字節碼並不難,並且它對咱們瞭解代碼背後的運行機制頗有幫助。

序列的排序

對序列的排序有兩種方法, list.sort()和sorted(). list.sort()會就地排序, 且返回一個None. 而sorted永遠返回一個列表, 即便傳入的是不可變對象.

二者都有兩個參數:
reverse
若是被設定爲 True,被排序的序列裏的元素會以降序輸出(也就是說把最大值看成最小值來排序)。這個參數的默認值是 False。
key
一個只有一個參數的函數,這個函數會被用在序列裏的每個元素上,所產生的結果將是排序算法依賴的對比關鍵字。好比說,在對一些字符串排序時,能夠用key=str.lower 來實現忽略大小寫的排序,或者是用 key=len 進行基於字符串長度的排序。這個參數的默認值是恆等函數(identity function),也就是默認用元素本身的值來排序。

>>> fruits = ['grape', 'raspberry', 'apple', 'banana']
>>> sorted(fruits)
>>> ['apple', 'banana', 'grape', 'raspberry']
>>> fruits
['grape', 'raspberry', 'apple', 'banana']
>>> sorted(fruits, reverse=True)
['raspberry', 'grape', 'banana', 'apple']
>>> sorted(fruits, key=len)
['grape', 'apple', 'banana', 'raspberry']
>>> sorted(fruits, key=len, reverse=True)
['raspberry', 'banana', 'grape', 'apple']
>>> fruits
['grape', 'raspberry', 'apple', 'banana']
>>> fruits.sort()
>>> fruits
['apple', 'banana', 'grape', 'raspberry']

bisect 模塊包含兩個主要函數,bisect 和 insort,兩個函數都利用二分查找算法來在有序序列中查找或插入元素。

bisect負責在一個序列中查找對應的應該插入的index, 後面能夠直接插入或者調用bisect.insort.

list的替換類型

array.array, set, deque等.

array.array就是C語言中數組的實現, 而且僅支持幾種數值的實現. 內部存儲的是字節信息, 能夠方便的輸入輸出到文件, 佔用內存和資源也較少.

memoryview

直接映射在內存上的數據結構, 可使用cast方式改變內存讀取解釋的類型.

>>> numbers = array.array('h', [-2, -1, 0, 1, 2])
>>> memv = memoryview(numbers) ➊
>>> len(memv)
5
>>> memv[0] ➋
-2
>>> memv_oct = memv.cast('B') ➌
>>> memv_oct.tolist() ➍
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
>>> memv_oct[5] = 4 ➎
>>> numbers
array('h', [-2, -1, 1024, 1, 2]) ➏

根據不一樣的內存解釋方法解釋內存中的字節, 'h'有符號整數, 'B'無符號整數.
因此把高字節改爲4, 則數值從0變到1024.

隊列

列表list本質上仍是順序排列的數據結構, 而不是相似鏈表的形式. 增長和刪除都有可能複製數據, 因此速度會受影響.

隊列的操做都是原子操做, 因此可使用在多線程環境中.

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息