測試開發之Python核心筆記(4):列表與元組

2020-08-09 12:33:25

元組和列表都是容器,容器裏面可以放各種型別的數據,同一個列表,允許放不同數據型別的數據。

4.1 宣告

宣告列表兩種方法:

inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"] # 測試框架pytest的3類組態檔,推薦pytest.ini,通常放到專案根目錄
li = list("requests")  # ['r', 'e', 'q', 'u', 'e', 's', 't', 's']
lis =  list(range(10))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lis = []  # 宣告空列表

宣告元組兩種方法:

tup = ('spring', 'summer', 'autumn', 'winter')  # ('spring', 'summer', 'autumn', 'winter')
tu = tuple("selenium")  # ('s', 'e', 'l', 'e', 'n', 'i', 'u', 'm')
t = ()  # 宣告空元組

4.2 相同點與區別

4.2.1 相同點

  • 都可以放置任意數據型別
  • 都支援負數索引
    • -1 表示最後一個元素,-2 表示倒數第二個元素
  • 都支援切片操作
    • l[1:3] ,返回列表中索引從1到2的子列表
    • tup[1:3] , 返回元組中索引從1到2的子元組
  • 都可以隨意巢狀
    • l = [[1, 2, 3], [4, 5]] , 列表的每一個元素也是一個列表
    • tup = ((1, 2, 3), (4, 5, 6)) ,元組的每一個元素也是一個元組

4.2.2 區別

  • 列表是動態的,可以隨意地增加、刪減或者改變元素,長度會變化
l = [1, 2, 'hello', 'world']
print(len(l))  # 原始長度是4
l.append("!")  # 增加
print(len(l))  # 長度程式設計5
l.remove(1)  # 刪除
l[0] = "Say"  # 修改
print(l)
  • 元組是靜態的,長度固定,無法增加、刪減或者改變

在Pycharm中輸入下面 下麪的程式碼,Pycharm會錯誤提示。執行時會報TypeError。

l = (1, 2, 'hello', 'world')
l[0] = "Say"  # TypeError: 'tuple' object does not support item assignment
  • 切片複製時,列表產生新物件而元組不產生新物件

    • 列表產生新物件
    l = [3, 2, 3, 7, 8, 1]
    l2=l[:]
    assert id(l2)==id(l)  # False,l2是個新物件
    
    • 元組不產生新物件
    t=(1,2,3,4,5)
    t2=t[:]
    assert id(t)==id(t2)  # True,l2和l1是同一個物件
    

4.4 列表和元組的操作

4.4.1 共有的操作

已知列表l = [3, 2, 3, 7, 8, 1] ,元組 t=(3,2,3,7,8,1)

# 統計,得到個數
l.count(3)
t.count(3)
# 查詢,第一次出現元素的下標
l.index(7)
t.index(7)
# 反轉,得到新的迭代器,通過tuple或者list轉成元組或者列表。
l2=list(reversed(l))
t2=tuple(reversed(t))  # 
# 排序,得到一個新列表,即便是元組也得到新列表
l3=sorted(l)
t3=sorted(t)
# 長度
l_length = len(l)
t_length = len(t)
# 壓縮,兩個列表或者兩個元組對應索引處的元素拼接
print(list(zip([1, 2, 3], ['a', 'b', 'c'])))  # 輸出[(1, 'a'), (2, 'b'), (3, 'c')]
print(tuple(zip((1, 2, 3), ('a', 'b', 'c'))))  # 輸出 ((1, 'a'), (2, 'b'), (3, 'c'))

4.4.2 列表獨有的操作

l = [1, 2, 'hello', 'world']
print(id(l))
l.append("!")  # 追加,原列表上追加
l.remove(1)  # 刪除,刪除特定元素a,沒有返回值
print(l.pop(0))  # 參數是下標,不帶參數預設刪除最後一個,並返回被刪除的值 
l[0] = "Say"
print(l)
l.sort()  # 原地排序,原列表排序
l.reverse()  # 原地反轉,原列表反轉
l.extend(["你好", "Hello"])
l.insert(2, "!")
print(id(l))

在Pycharm中,按兩下shift,輸入list或者tuple,並勾選include non-project items,可以查詢Python內建的列表和元祖操作的所有方法及具體用法。

4.5 賦值與修改

4.5.1 列表賦值與修改

由於列表是可變的,當多個變數指向同一個列表物件時,當列表物件變化後,所有變數的值都會變化。

>>> l1 = [1, 2, 3]
>>> l2=l1
>>> l1
[1, 2, 3]
>>> l2
[1, 2, 3]
>>> l1.append(4)  # 在原物件上修改
>>> l1
[1, 2, 3, 4]
>>> l2  # l2也變了
[1, 2, 3, 4]

4.5.2 元組的賦值與修改

由於元組是不可變的,當對元組進行修改時,是生成新的元組,不是在原來的元組物件上修改。因此對元組的改變,只會影響原來變數的值。

>>> t1=(1, 2, 3)
>>> t2=t1
>>> t2
(1, 2, 3)
>>> t1=t1+(5,)  # 會產生新物件,t1指向新物件
>>> t1
(1, 2, 3, 5)
>>> t2  # t2不變
(1, 2, 3)

可見,在Python中:

  • 變數的賦值,只是表示讓變數名指向了某個物件,而一個物件,可以被多個變數指向。
  • 可變物件(列表、字典、集合等)的改變,會影響所指向該物件的變數。
  • 對於不可變物件(字串、整型、元組等等),所有指向該物件的變數的值總是一樣的,也不會改變。但是通過某些操作(例如+=)更新不可變物件的值時,會返回一個新物件。

4.6 使用場景

  • 如果儲存的數據不變,適合用元組,比如存放經緯度、四季、12個月份等
  • 如果儲存的數據是可變的,適合用列表。

4.7 列表解析

根據已有字串、列表、元組、字典生產新列表。數據全部放在記憶體。

  • [x*x for x in [3, 2, 3, 7, 8, 1]]
  • [x*x for x in (3, 2, 3, 7, 8, 1)]
  • [x * x for x in range(1, 11)]
  • [x * x for x in range(1, 11) if x % 2 == 0] # 可以使用if語句
  • [m + n for m in ‘ABC’ for n in ‘XYZ’]
  • [item for item in lst if lst.count(item) == 1] # 保留唯一值
  • [k + ‘=’ + v for k, v in d.items()] #字典鍵值對用等號連線
  • [round(random(),2) for _ in range(10)] # 生成隨機小數
  • [w.lower() for w in a if isinstance(w,str)] # 全部轉爲小寫

列表解析常與 if、for、巢狀 for、map、lambda 等結合使用。

4.8 序列解包

可以將包含N個元素的元組或者列表,分解成N個獨立的變數。

data = ['ACME', 50, 19.8, (2020, 5, 23)]
name, shares, price, date = data
print(name)  # 輸出ACME
print(shares)   # 輸出50
print(price)  # 輸出19.8
print(date)  # 輸出(2020, 5, 23)
name, shares, price, (year, month, day) = data
print(year, month, day)   # 輸出2020 5 23

當元素的數量和變數的數量不匹配時會報錯。

解包操作時,對於不關心的數據可以丟,只需要在相應的位置上用一個用不到的變數名即可,例如_。

data = ['ACME', 50, 19.8, (2020, 5, 23)]
_, shares, price, _ = data  # 丟棄列表中的第一個元素和最後一個元素。

還可以從N個元素的元組或者列表中,分解出小於N個元素。例如多名大衆評委給選手打分組成的列表,去掉最高分和一個最低分之後,計算總分。

def total_score(scores):
    minimum, *middles, maximum = sorted(scores)  # 排序後的成績,中間所有成績放入到middles列表中
    return sum(middles)


if __name__ == '__main__':
    scores = [1, 2, 3, 4, 5, 6, 7, 8]
    print(total_score(scores))

4.9 all 和 any

all(iterable)函數,接受一個迭代器,如果迭代器的所有元素都爲真,返回 True,否則返回 False:

all([1,0,3,6])  # False
all([1,2,3])  # True
all((1, 0, 3, 6))  # False

any(iterable)函數,接受一個迭代器,如果迭代器裡有一個元素爲真,返回 True,否則返回 False:

any([0,0,0,[]])  # False
any([0,0,1])  # True
any((1, 0, 0, 0))  # True

4.10 面試題

  1. 判斷 list 內有無重複元素

如果列表中有重複元素,則返回True,沒有重複元素則返回False。

def is_duplicated(lst):  # 定義一個函數,函數名是is_duplicated,參數是lst
    for x in lst:
        if lst.count(x) > 1:   # 藉助count統計功能
            return True
    return False


def is_duplicated_2(lst):
    return len(li) != len(set(li))  # 藉助集合set具有去重功能


if __name__ == '__main__':
    li = [1, -2, 3, 4, 1, 2]
    print(is_duplicated(li))
    print(is_duplicated_2(li))
  1. 找出列表中的所有重複元素

藉助count統計個數,大於1個的說明是重複元素,新增到新的列表中,返回。

def find_duplicated(lst):
    li = []
    for i in lst:
        if lst.count(i) > 1 and i not in li:
            li.append(i)
    return li


if __name__ == '__main__':
    my_list = [1, -2, 3, 4, 1, 2]
    print(find_duplicated(my_list))
  1. 找出列表出現次數最多的元素

看到題目中出現了次數,就想count方法,看到「最」,想到 Python 內建的max 函數。

def find_max_count(lst):
    if lst is None or len(lst) == 0:
        return None
    return max(lst, key=lambda x: lst.count(x))


if __name__ == '__main__':
    my_list = [1, -2, 3, 2, 4, 1, 2]
    my_list2=[]
    print(find_max_count(my_list))
    print(find_max_count(my_list2))

上面的辦法在最多的元素有多個時,預設只返回一個。那麼如何可以返回多個呢?

思路是:先找出最多元素的個數是幾個,比如n個,然後在列表中找哪些元素的個數是n個。

def find_max_count(lst):
    if lst is None or len(lst) == 0:
        return None
    max_freq_element = max(lst, key=lambda x: lst.count(x))
    max_freq = lst.count(max_freq_element)
    li = []
    for l in lst:
        if lst.count(l) == max_freq and l not in li:
            li.append(l)
    return li


if __name__ == '__main__':
    my_list = [1, -2, 3, 2, 4, 1, 2]
    my_list2 = []
    print(find_max_count(my_list))
    print(find_max_count(my_list2))