python+=和排序

2021-01-15 牛建邦

#增量賦值運算符+= 和*= 的表現取決於它們的第一個操作對象。+= 背後的特殊方法是__iadd__(用於「就地加法」)。但是如果一個類沒有實現這個方法的話,Python 會退一步調用__add__。可變序列一般都實現了__iadd__ 方法,因此+= 是就地加法。而不可變序列根本就不支持這個操作,對這個方法的實現也就無從談起。*=相對應的是__imul__

l = [1, 2, 3]

print(id(l))#2011053505408  剛開始時列表的ID。

l *= 2

print(id(l))#2011053505408 運用增量乘法後,列表的ID 沒變,新元素追加到列表上。

t = (1, 2, 3)

print(id(t))#2011054477376 元組最開始的ID。

t *= 2

print(id(t))#2011054746688 運用增量乘法後,新的元組被創建。

#對不可變序列進行重複拼接操作的話,效率會很低,因為每次都有一個新對象,而解釋器需要把原來對象中的元素先複製到新的對象裡,然後再追加新的元素.str 是一個例外,因為對字符串做+= 實在是太普遍了,所以CPython 對它做了優化。為str 初始化內存的時候,程序會為它留出額外的可擴展空間,因此進行增量操作的時候,並不會涉及複製原有字符串到新位置這類操作。

 

#一個關於+=的謎題

>>> t = (1, 2, [30, 40])

>>> t[2] += [50, 60]

"""

到底會發生下面4 種情況中的哪一種?

a. t 變成(1, 2, [30, 40, 50, 60])。

b. 因為tuple 不支持對它的元素賦值,所以會拋出TypeError 異常。

c. 以上兩個都不是。

d. a 和b 都是對的。

"""

"""

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment

>>> t

(1, 2, [30, 40, 50, 60])

"""

由此可見a和b都是對的,使用extend()就不會提示異常 

t = (1, 2, [30, 40])

t[2].extend([50, 60])

print(t)

 

import dis#Python代碼是先被編譯為Python字節碼後,再由Python虛擬機來執行Python字節碼(pyc文件主要就是用於存儲字節碼指令 的)。一般來說一個Python語句會對應若干字節碼指令,Python的字節碼是一種類似彙編指令的中間語言,但是一個字節碼指令並不是對應一個機器指 令(二進位指令),而是對應一段C代碼,而不同的指令的性能不同,所以不能單獨通過指令數量來判斷代碼的性能,而是要通過查看調用比較頻繁的指令的代碼來 確認一段程序的性能。dis模塊主要是用來分析字節碼的一個內置模塊,經常會用到的方法是dis.dis([bytesource]),參數為一個代碼塊,可以得到這個代碼塊對應的字節碼指令序列。

dis.dis('s[a] += b')

"""

 1           0 LOAD_NAME                0 (s)

              2 LOAD_NAME                1 (a)

              4 DUP_TOP_TWO

              6 BINARY_SUBSCR                   #將s[a] 的值存入TOS(Top Of Stack,棧的頂端)

              8 LOAD_NAME                2 (b)

             10 INPLACE_ADD                     #計算TOS += b。這一步能夠完成,是因為TOS 指向的是一個可變對象

             12 ROT_THREE

             14 STORE_SUBSCR                    #s[a] = TOS 賦值。這一步失敗,是因為s 是不可變的元組

             16 LOAD_CONST               0 (None)

             18 RETURN_VALUE

"""

#不要把可變對象放在元組裡面; 增量賦值不是一個原子操作。我們剛才也看到了,它雖然拋出了異常,但還是完成了操作; 查看Python 的字節碼並不難,而且它對我們了解代碼背後的運行機制很有幫助。

 

#list.sort 方法會就地排序列表,也就是說不會把原列表複製一份。這也是這個方法的返回值是None 的原因,提醒你本方法不會新建一個列表。在這種情況下返回None 其實是Python 的一個慣例:如果一個函數或者方法對對象進行的是就地改動,那它就應該返回None,好讓調用者知道傳入的參數發生了變動,而且並未產生新的對象。例如,random.shuffle 函數也遵守了這個慣例。

#內置函數sorted,它會新建一個列表作為返回值。這個方法可以接受任何形式的可迭代對象作為參數,甚至包括不可變序列或生成器。而不管sorted 接受的是怎樣的參數,它最後都會返回一個列表。

#不管是list.sort 方法還是sorted 函數,都有兩個可選的關鍵字參數:reverse如果被設定為True,被排序的序列裡的元素會以降序輸出(也就是說把最大值當作最小值來排序)。這個參數的默認值是False;key一個只有一個參數的函數,這個函數會被用在序列裡的每一個元素上,所產生的結果將是排序算法依賴的對比關鍵字。比如說,在對一些字符串排序時,可以用key=str.lower 來實現忽略大小寫的排序,或者是用key=len 進行基於字符串長度的排序。這個參數的默認值是恆等函數(identity function),也就是默認用元素自己的值來排序。

#可選參數key 還可以在內置函數min() 和max() 中起作用。另外,還有些標準庫裡的函數也接受這個參數,像itertools.groupby() 和heapq.nlargest() 等。

#Python 的排序算法——Timsort——是穩定的,意思是就算兩個元素比不出大小,在每次排序的結果裡它們的相對位置是固定的。

fruits = ['grape', 'raspberry', 'apple', 'banana']

print(sorted(fruits))#['apple', 'banana', 'grape', 'raspberry'] 新建了一個按照字母排序的字符串列表。

print(fruits)#['grape', 'raspberry', 'apple', 'banana'] 原列表並沒有變化。

print(sorted(fruits, reverse=True))#['raspberry', 'grape', 'banana', 'apple'] 按照字母降序排序。

print(sorted(fruits, key=len))#['grape', 'apple', 'banana', 'raspberry'] 新建一個按照長度排序的字符串列表。因為這個排序算法是穩定的,grape 和apple 的長度都是5,它們的相對位置跟在原來的列表裡是一樣的。

print(sorted(fruits, key=len, reverse=True))#['raspberry', 'banana', 'grape', 'apple'] 按照長度降序排序的結果。結果並不是上面那個結果的完全翻轉,因為用到的排序算法是穩定的,也就是說在長度一樣時,grape 和apple 的相對位置不會改變。

print(fruits) #['grape', 'raspberry', 'apple', 'banana'] 直到這一步,原列表fruits 都沒有任何變化。

print(fruits.sort()) #對原列表就地排序,返回值None 會被控制臺忽略。

print(fruits) #['apple', 'banana', 'grape', 'raspberry'] 此時fruits 本身被排序。

 

"""

數字                格式                 輸出                 描述

3.1415926           {:.2f}              3.14                保留小數點後兩位

3.1415926           {:+.2f}             +3.14               帶符號保留小數點後兩位

-1                  {:+.2f}             -1.00               帶符號保留小數點後兩位

2.71828             {:.0f}              3                   不帶小數

5                   {:0>2d}             05                  數字補零 (填充左邊, 寬度為2)

5                   {:x<4d}             5xxx                數字補x (填充右邊, 寬度為4)

10                  {:x<4d}             10xx                數字補x (填充右邊, 寬度為4)

1000000             {:,}                1,000,000           以逗號分隔的數字格式

0.25                {:.2%}              25.00%              百分比格式

1000000000          {:.2e}              1.00e+09            指數記法

13                  {:>10d}             13                  右對齊 (默認, 寬度為10)

13                  {:<10d}             13                  左對齊 (寬度為10)

13                  {:^10d}             13                  中間對齊 (寬度為10)

11                  '{:b}'.format(11)   1011

                    '{:d}'.format(11)   11

                    '{:o}'.format(11)   13

                    '{:x}'.format(11)   b

                    '{:#x}'.format(11)  0xb

                    '{:#X}'.format(11)  0XB                 進位

^, <, > 分別是居中、左對齊、右對齊,後面帶寬度, : 號後面帶填充的字符,只能是一個字符,不指定則默認是用空格填充。

+ 表示在正數前顯示 +,負數前顯示 -;  (空格)表示在正數前加空格

b、d、o、x 分別是二進位、十進位、八進位、十六進位。

"""

 

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

#用bisect來搜索,bisect(haystack, needle) 在haystack(乾草垛)裡搜索needle(針)的位置,該位置滿足的條件是,把needle 插入這個位置之後,haystack 還能保持升序。也就是在說這個函數返回的位置前面的值,都小於或等於needle 的值。其中haystack 必須是一個有序的序列。你可以先用bisect(haystack, needle) 查找位置index,再用haystack.insert(index,needle) 來插入新值。但你也可用insort 來一步到位,並且後者的速度更快一些。

#排序集合模塊(http://code.activestate.com/recipes/577197-sortedcollection/),模塊裡集成了bisect 功能,但是比獨立的bisect 更易用。

#在有序序列中用bisect 查找某個元素的插入位置

import bisect

import sys

 

HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]

NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]

ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'

 

def demo(bisect_fn):

    for needle in reversed(NEEDLES):

        position = bisect_fn(HAYSTACK, needle) #用特定的bisect 函數來計算元素應該出現的位置。

        offset = position * ' |' #利用該位置來算出需要幾個分隔符號。

        print(ROW_FMT.format(needle, position, offset)) #把元素和其應該出現的位置列印出來。

    

if __name__ == '__main__':

    if sys.argv[-1] == 'left': #根據命令上最後一個參數來選用bisect 函數。

        bisect_fn = bisect.bisect_left

    else:

        bisect_fn = bisect.bisect

    print('DEMO:', bisect_fn.__name__) #把選定的函數在抬頭列印出來。

    print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))

    demo(bisect_fn)

"""

DEMO: bisect_right

haystack ->  1  4  5  6  8 12 15 20 21 23 23 26 29 30

31 @ 14  | | | | | | | | | | | | | |31

30 @ 14  | | | | | | | | | | | | | |30

29 @ 13  | | | | | | | | | | | | |29

23 @ 11  | | | | | | | | | | |23

22 @  9  | | | | | | | | |22

10 @  5  | | | | |10

 8 @  5  | | | | |8

 5 @  3  | | |5

 2 @  1  |2

 1 @  1  |1

 0 @  0 0

 """

#bisect 的表現可以從兩個方面來調教。首先可以用它的兩個可選參數——lo 和hi——來縮小搜尋的範圍。lo 的默認值是0,hi的默認值是序列的長度,即len() 作用於該序列的返回值。

bisect 函數其實是bisect_right 函數的別名,後者還有個姊妹函數叫bisect_left。它們的區別在於,bisect_left 返回的插入位置是原序列中跟被插入元素相等的元素的位置,也就是新元素會被放置於它相等的元素的前面,而bisect_right 返回的則是跟它相等的元素之後的位置。

 

#bisect 可以用來建立一個用數字作為索引的查詢表格,比如說把分數和成績對應起來。根據一個分數,找到它所對應的成績#它們可以在很長的有序序列中作為index 的替代,用來更快地查找一個元素的位置。

import bisect

def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):

    i = bisect.bisect(breakpoints, score)

    return grades[i]

 

print([grade(score) for score in [33, 99, 77, 70, 89, 90, 100]])#['F', 'A', 'C', 'C', 'B', 'A', 'A']

 

#insort(seq, item) 把變量item 插入到序列seq 中,並能保持seq 的升序順序。

#insort 可以保持有序序列的順序

import bisect

import random

SIZE=7

random.seed(1729)

my_list = []

for i in range(SIZE):

    new_item = random.randrange(SIZE*2)

    bisect.insort(my_list, new_item)

    print('%2d ->' % new_item, my_list)

"""

10 -> [10]

 0 -> [0, 10]

 6 -> [0, 6, 10]

 8 -> [0, 6, 8, 10]

 7 -> [0, 6, 7, 8, 10]

 2 -> [0, 2, 6, 7, 8, 10]

10 -> [0, 2, 6, 7, 8, 10, 10]

"""

#new_item = random.randrange(SIZE * 2)返回的隨機數列一直是相同不變的,後來發現random.randrange()產生的隨機數之所以會固定以因為random.seed()這個方法的原因。

 

#insort 跟bisect 一樣,有lo 和hi 兩個可選參數用來控制查找的範圍。它也有個變體叫insort_left,這個變體在背後用的是bisect_left。

#random.seed() 會改變隨機生成器的種子;傳入的數值用於指定隨機數生成時所用算法開始時所選定的整數值,如果使用相同的seed()值,則每次生成的隨機數都相同;如果不設置這個值,則系統會根據時間來自己選擇這個值,此時每次生成的隨機數會因時間的差異而有所不同。

import random

 

random.seed(1)

for i in range(1, 9):

    print(random.randrange(10), end=' ')#2 9 1 4 1 7 7 7

 

print('\n')

 

for i in range(1, 9):

    random.seed(1)

    print(random.randrange(10), end=' ')#2 2 2 2 2 2 2 2

 

print('\n')

 

random.seed(1)

print(random.randrange(10), end=' ')#2

print('\n')

for i in range(1, 9):

    print(random.randrange(10), end=' ')#9 1 4 1 7 7 7 6

#random.seed()的返回值是None;但是我覺得他會隱藏的構建一個類似列表的數據結構,這個列表的長度應該是特別大的(我曾將循環次數加到6000次發現返回的結果依舊是相同的),根據你在seed()中傳入的參數不同,seed會構建一個元素不同的列表,而random.randrange()會迭代這個列表,按照列表的順序將每個元素依次提出來作為隨機數算法的初始值。這也就是為什麼在例子1中設置了一個seed()後每次返回的隨機序列都是相同的,以在例子3的結果和例子1的結果也是相同的。

#而當我把seed()放進循環中去的時候,依舊會構建一個list,只不過list在循環裡面,每次都會被重置,而當random.randrange()每次去迭代的時候由於list一直被循環重置,所以random.randrange()在迭代列表時的指針也就會一直被重定向到首元素,也就一直去使用list中的第一個元素作為隨機數算法的開始整數,因此得到的隨機數序列就一直是基於list中的第一個元素求得的隨機數算法的值2。

 


相關焦點

  • python入門第四課:列表的排序、元素遍歷
    本教程使用的課本是《Python編程:從入門到實踐》,作者:[美] Eric Matthes本節介紹列表的操作,包括列表的排序、元素遍歷等操作。一、列表的排序有時候我們需要按升序或降序排列列表的元素,可以用sort()方法,sort方法默認是升序,如果加個參數,變成sort(reverse=True)就會按降序排列,見下面的代碼:Mylists = [2,58,64,21,33,5,8,9,4,15,23,45,60,88
  • Python,Numpy,Pandas……數據科學家必備排序技巧
    全文共6168字,預計學習時長12分鐘對數據進行分類整理是數據科學家和數據工程師的基礎工作。Python會提供許多內置庫,優化排序選項。有些庫甚至可以同時在GPU上運行。令人驚奇的是,一些排序方法並沒有使用之前所述的算法類型,其他方法的執行效果也不如預期。選擇使用哪種庫和哪類排序算法著實難辦,因為算法的執行變化很快。
  • 用Python實現職工信息管理系統
    想要實現一個職工管理系統首先我們看一下想要實現什麼功能最基礎的增刪改查肯定要實現的然後增加一下數據顯示、數據排序、數據統計功能下面直接上代碼1.排序函數```pythonglobal datadata = sorted(data, key=lambda x: x[1])```6.
  • python數據類型總結——列表
    python用列表或元組來幫助我們。python列表一系列元素組成一個集合,可能通過索引,對每個元素進行訪問。這種數據類型統稱為序列。在python中,字符串就是一種序列。如下:拼接和重複in 和 not in 也同樣可以。
  • python卡方檢驗 - CSDN
    假設y為目標變量,取值為好和壞,x為特徵變量取值為高、中、低。      1、先計算y和x的實際值列聯表,如下圖:      上面計算的卡方值=45.41,查卡方分布表可知P(卡方值>45.41)<<0.05,所以有理由拒絕y和x不相關的原假設,即y和x有較強的相關性。
  • Python數據類型串講(中)
    python中的內建序列有6種:列表、元祖、字符串、Unicode字符串、xrange對象、buffer對象,其中列表和元祖是最常見的序列,應重點掌握。字符串在上一篇文章中已簡單介紹,下面將以字符串為例,對序列的通用操作進行詳講。
  • python高階函數:map、filter、reduce的替代品
    sorted函數是python的內置函數,它的可選參數key用於提供一個函數,它可以將函數應用到各個元素上進行排序。根據單詞長度,使用sorted函數對一個列表進行排序。其中將len函數傳給key參數,具體示例如下:這裡需要特別提示一下,任何單參數函數都能作為key參數的值。
  • 不懂NumPy 算什麼 Python 程式設計師?|CSDN 博文精選
    構造複數的方法如下:>>> complex(2,5)(2+5j)數組操作(1) 切片和索引對於一維數組的索引和切片,numpy和python的list一樣,甚至更靈活。排序不是 numpy 數組的強項,但 python 數組的排序速度依然只能望其項背。
  • 雲計算開發實例:Python3 拓撲排序
    對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。
  • python之lambda定義匿名函數實現對list列表排序
    list.sort()函數1、定義一個元素類型為整型的list列表,然後排序運行結果是:排序之前,list1列表中的內容: [10, 50, 20, 57, 90]排序之後,>發現並沒有排序,提示錯誤,原因是:"<"不支持字典與字典之間的排序。
  • Python 三十大實踐、建議和技巧
    1、使用 python 3由於官方從2020年1月1日起就停止了對python2.7的更新支持,因此本教程的大部分例子都只能在python 3環境下運行。如果你仍然在使用2.7版本,請先升級到python 3。2、檢查並使用滿足需求的最小python版本你可以在代碼中檢查Python 版本,以確保你的代碼使用者沒有使用不兼容的版本運行腳本。
  • python 卡方檢驗專題及常見問題 - CSDN
    假設y為目標變量,取值為好和壞,x為特徵變量取值為高、中、低。      1、先計算y和x的實際值列聯表,如下圖:      上面計算的卡方值=45.41,查卡方分布表可知P(卡方值>45.41)<<0.05,所以有理由拒絕y和x不相關的原假設,即y和x有較強的相關性。
  • 雲計算開發實例:Python3歸併排序
    歸併排序(英語:Merge sort,或mergesort),是創建在歸併操作上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
  • 11個python列表方法全面解析!
    lk = ls #這不是複製,而是給列表ls新關聯一個引用,即增加一個別名,ls和lt指向同一個內存地址。ls = [1,2,3,5,4,5,5,5,5,"python"]ls.count(5) #統計列表ls中 5 出現的次數5ls.count(0)#列表ls中無0元素ls.count("python")
  • Python數據類型之列表list
    # 列表是python中最基本的數據結構,它是一個有序序列,序列中的每個元素都分配一個數字(位置,索引)# 1、我們可以使用 方括號,中括號[]來創建列表# 2、我們可以直接將序列放在list(seq)
  • Python和人工智慧有什麼關係?Python 和人工智慧的區別是什麼?
    人工智慧是一個大的範疇,包括很多方面的應用,比如機器學習,在機器學習中的回歸算法,它們是通過統計分析所有數據來建立多因式,然後求解式子,而在這個過程中程式語言起到的作用是清洗數據、處理數據、建立關係求解結果的作用,python適用於數據清洗且學習成本低,所以在一定程度上,好一部分人傾向於將python應用於人工智慧應用領域。
  • python教程第三課:python IDE之jupyter notebook詳細教程講解
    這次我們先來講一下如何使用Notebook進行python的代碼編寫1、 首先我們在系統安裝裡面找到Anaconda,裡面有一項Jupyter Notebook,點擊它以後將打開下面的界面, 這個界面看網址就知道,是在本地啟動打開的一個頁面,那麼除了可以從anaconda裡面打開,也可以在CMD窗口裡面輸入jupyter notebook
  • python實踐分享:關於排序算法,怎麼選擇sort()或者sorted()?
    各種排序算法以及它們的時間複雜度分析是很多企業面試人員在面試時候經常會問到的問題,這也不難理解,在實際的應用過程中確實會遇到各種需要排序的情況,如按照字母表輸出一個序列、對記錄的多個欄位排序等。還好,Python中的排序相對簡單,常用的函數有 sort()和sorted()兩種。這兩種函數並不完全相同,各有各的用武之地。我們來具體分析一下。
  • 2019年必知的10大頂級Python庫
    在 TensorFlow 創建的所有庫都是用 C 和 C++編寫的,但是,它有一個複雜的前端,是用 python 實現的。你的 python 代碼將被編譯,然後在使用 C 和 C++構建的 TensorFlow 分布式執行引擎上執行。實際上,TensorFlow 的應用是無限的,這就是它美妙的地方。
  • 快速介紹Python數據分析庫pandas的基礎知識和代碼示例
    本附註的結構:導入數據導出數據創建測試對象查看/檢查數據選擇查詢數據清理篩選、排序和分組統計數據首先,我們需要導入pandas開始:import pandas as pd導入數據使用函數pd.read_csv直接將CSV轉換為數據格式。注意:還有另一個類似的函數pd。read_excel用於excel文件。