程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

【Python】深度講解序列和字典,復習必備-太細啦

編輯:Python

文章目錄:

    • 序列
      • 序列的概念
      • 標准類型操作符
      • 序列類型操作符
      • 序列的切片操作
        • 翻轉字符串
      • 序列內建函數
          • len函數
          • max函數 -O(N)
          • min函數 -O(N)
          • sorted函數
          • sum函數
          • enumerate函數
          • zip函數
    • 字符串
      • 三種引號的區別
      • 理解字符串 "不可變"
      • 字符串的標准類型操作符
      • 只適用於字符串的操作符
      • 原始字符串(raw strings)
      • repr函數和反引號操作符
      • string 模塊常用函數
        • 注意點:記得字符串是不可變對象, 只能生成新字符串
          • join函數
          • **split函數**
          • **startswith函數 和 endswith函數**
          • strip函數
          • ljust rjust center函數
          • find函數
          • **replace函數**
          • **isalpha函數 和 isdigit函數**
          • **lower和upper函數**
      • 關於結束符
    • 列表 []
      • 使用切片操作符訪問列表元素
      • 列表常用操作
        • append: 追加元素
        • 刪除指定下標元素 del
        • 按值刪除元素 remove
        • 列表比較操作
        • in/not in: 判斷一個元素在不在列表中
        • 連接操作符(+): 連接兩個列表
        • extend: 列表連接
        • 重復操作符(*):
        • insert:任意位置插入
        • reverse:原地翻轉列表
        • sort:原地排序
        • count:統計元素出現次數
        • index:相當於查找,返回下標
        • pop:刪除列表一個元素
          • 以上函數注意點:
      • 基於列表的堆棧
      • 基於列表的隊列
      • 列表的深拷貝/淺拷貝(選學)
        • 總結:
        • 如何進行深拷貝:
    • 元組 ()
      • 元組常用操作
      • 默認集合類型
      • 理解元組的 "不可變"
        • 元組不可以替代的原因
    • 字典{}
      • 創建字典
      • 訪問字典中的元素
      • 修改字典元素
      • 刪除字典元素
      • 常用內置函數
        • in / not in:
        • len函數
        • hash函數
        • keys
        • values
        • items
    • 集合(set)
      • 集合基本操作
        • 取交集& 並集| 差集 - 對稱差集^
        • 數據去重 使用->set

本文章主要講解內容:

理解Python的序列的基本概念.
掌握字符串/列表/元組/字典的基本使用方法.
理解列表和元組的區別和各自的應用場景.
理解Python中的深拷貝和淺拷貝.
理解字典 “鍵值對” 這樣的概念


序列

序列的概念

包含若干個元素, 元素有序排列, 可以通過下標訪問到一個或者多個元素. 這樣的對象, Python中統一稱為序列(Sequence).

Python中的以下對象都屬於序列

  • 字符串
  • 列表
  • 元組

同是序列, 他們的使用方式有很多相通之處


注意:序列裡面的元素的順序很重要,因為比較是按順序比

a = [1,2,3]
b = [3,2,1]
print(a ==b) #False

標准類型操作符

下列標准類型操作符, 大多數情況下都是適用於序列對象的(少部分特例是, 序列中保存的元素不支持標准類型操作符).


序列類型操作符

  • in/not in: 判定一個元素是否存在於序列中, 返回布爾值.
a = [1,2,3,4]
print(3 in a) #True
print(3 not in a) #False
  • 連接操作符(+): 把兩個相同類型的序列進行連接.
a = [1,2,3,4]
b = [5,6]
print(a+b) #返回一個新列表,包含了a和b的所有元素[1, 2, 3, 4, 5, 6]
  • 連接操作符往往並不高效(新創建對象, 把原有對象拷貝進去). 對於列表, 推薦使用extend來完成這樣的操作; 對於字符串, 推薦使用join這樣的方法.
a = [1,2,3,4]
b = [5,6]
a.extend(b) #相當於把b的元素都插入到a的後面
print(a) #[1, 2, 3, 4, 5, 6]
  • 重復操作符(*): 讓一個序列重復N次.
a =[1,2,3]
print(a*3) #[1, 2, 3, 1, 2, 3, 1, 2, 3]

序列的切片操作

  • 切片操作符([ ], [A:B], [A:B:C]): 通過下標訪問其中的某一個元素, 或者某個子序列

  • 正數的索引以序列的起始位置作為起點, 負數的索引以序列的結束位置做為起點.
  • 試圖訪問一個越界的索引, 會引發異常(可以簡單理解成程序執行出錯)
a =[1,2,3]
print(a[100])
#執行結果:
IndexError: list index out of range

關於切片:左閉右開區間

方式1:[:] 左右兩個端點都不寫值,截取的是整個序列的元素,從頭到尾

a =[1,2,3,4,5]
print(a[:])#[1, 2, 3, 4, 5]

方式2:[A:B]

元素下標取值范圍: [A,B)

a =[1,2,3,4,5]
print(a[1:3]) #[2,3] 截取下標[1,3)的元素 
print(a[1:-1]) #[2,3,4] 截取下標[1,-1)的元素
print(a[:3]) #[1,2,3] 截取下標[0,3)的元素 
print(a[1:]) #[2,3,4,5] 截取下標[1,-1)的元素

如果左邊端點不寫,默認從0開始, 右邊端點不寫,默認截取到最後一個位置(即:-1位置)


方式3:[A:B:C] 第三個參數表示步長,即每隔多少個元素截取一個

  • 擴展切片操作[::] 除了可以表示子序列的起始和結束位置, 還可以表示 “步長”

例子:

a = [1,2,3,4,5]
print(a[::2]) #每兩個元素截取一個
#執行結果:
[1,3,5]

翻轉字符串

字符串翻轉, 這是一個非常基礎, 也是筆試面試中會經常出現的一個題目. 我們學過C/C++, 有三種方法來解決這個問題.

方法1:首尾指針

char str[] = "abcdefg";
char* beg = str;
char* end = str + strlen(str);
while (beg < end) {

swap(*beg++, *--end);
}

方法2:棧

char str[] = "abcdefg";
Stack stack;
char* p = str;
while(p) {

stack.push(*p++);
}
int index = 0;
while(!stack.empty()){

str[index++] = stack.top();
stack.pop();
}

方法3:使用reverse + 迭代器翻轉

#include <algorithm>
char str[] = "abcdefg";
std::reverse(str, str + strlen(str)); //指針就是天然的迭代器

python的做法:

a = "abcdefg"
print(a[::-1])

這個代碼的含義:

a[::-1] -1表示往前走,從後往前拿元素

a = [1,2,3,4,5,6]
print(a[::-1]) #[6,5,4,3,2,1]
#含義
從-1位置往前走,先走到下標為-1位置,然後從6開始往前走

對於切片語法來說, 下標越界也沒關系. 因為取的是前閉後開區間,區間裡的元素, 能取到多少就取到
多少.


序列內建函數

len函數

len: 返回序列的長度.

a = [2,3,4,5]
print(len(a)) #4
b = "hello"
print(len(b)) #5

max函數 -O(N)

max: 返回序列中的最大值

a = [2,3,4,5]
print(max(a)) #5
b = "helloz"
print(max(b)) #z

min函數 -O(N)

min: 返回序列中的最小值

a = [2,3,4,5]
print(min(a)) #2
b = "helloz"
print(min(b)) #e

sorted函數

sorted: 排序. 這是一個非常有用的函數. 返回一個有序的序列(輸入參數的副本).

a = ['abc','acb','a','b']
print(sorted(a)) #['a', 'abc', 'acb', 'b']
a = [5,3,3,1,5]
print(sorted(a)) #[1, 3, 3, 5, 5]

sorted可以支持自定制排序規則


sum函數

sum: 序列中的元素求和(要求序列中的元素都是數字)

a = [1,2,3,4,5]
print(sum(a)) #15
a= [1,'a']
print(sum(a)) #報錯 unsupported operand type(s) for +: 'int' and 'str'

enumerate函數

enumerate: 同時枚舉出序列的下標和值 可以避免很多丑陋的代碼.

例如:找出元素在列表中的下標

a = [1,2,3,4,5]
def Find(input_list,x):
for i in range(0,len(input_list)):
if input_list[i] == x:
return i
else: #此處的else和for搭配
return None
print(Find(a,2)) #1 下標為1

這裡用for循環寫的就不夠優雅,使用enumerate函數就可以寫的很優雅

a = [1,2,3,4,5]
def Find(input_list,x):
for i ,item in enumerate(input_list):
if item == x:
return i
else: #此處的else和for搭配
return None
print(Find(a,2)) #1 下標為1

zip函數

zip: 這個函數的本意是 “拉鏈”,

x = [1,2,3]
y = [4,5,6]
z = [7,8,9,10] #多余的10不要,3行3列
print(zip(x,y,z)) #直接打印是對象的id <zip object at 0x000001581CFE7748>
#把執行結果強轉為list,列表
print(list(zip(x,y,z))) #[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
#直觀打印
for i in zip(x,y,z):
print(i)
#執行結果:
(1, 4, 7)
(2, 5, 8)
(3, 6, 9)

zip可以理解為行列互換


zip的一個比較常見的用法, 就是構造字典

key = ('name','id','score')
value =('Mango','2022','99')
d = dict(zip(key,value)) #執行結果轉為一個字典
print(d) # {'name': 'Mango', 'id': '2022', 'score': '99'}

字符串

三種引號的區別

  • Python中單引號(')字符串和雙引號(")字符串是等價的. 而不像有些編程語言(比如Linux Shell), 轉義字符只在雙引號中有效.
  • Python中使用 “原始字符串” 來完成這樣的功能
  • 三引號(‘’'/“”")相比於前兩種的區別是, 可以跨多行.
    • 三引號還可以作為文檔字符串

理解字符串 “不可變”

  • 字符串實際上是不可變的對象. 你不能修改這個字符串, 只能創建一個新的字符串.
a = 'abcd'
a[0] ='z' #TypeError: 'str' object does not support item assignment
a = 'z'+a[1:]
print(a) #zbcd

字符串的標准類型操作符

  • ==, !=, <, <=, >, >= 這些操作符的行為前面已經提到過.

  • 需要記得字符串比較大小是按照字典序.

a = 'abc'
b = 'ab'
print(a != b) #True
print(a < b) #False 按照字典序比較

  • in/not in的規則和序列的規則一致.
a = 'abc'
print('a' in a) #True
print('z' in a) #False
  • 切片操作和序列規則一致
a = 'abcd'
print(a[1:2]) #b
print(a[:2]) #ab
print(a[1:]) #bcd
print(a[:]) #abcd
print(a[::2]) #ac

只適用於字符串的操作符

  • %: 格式化替換.
x = 1
print('x = %d' %x) # x = 1

x = 10
y = 20
a = 'x = %d y = %d' %x #缺少參數:報錯 TypeError: not enough arguments for format string
#正解:
x = 10
y = 20
a = 'x = %d y = %d' %(x,y)

推薦寫法:加前綴f

x = 10
y = 20
a = f'x = {
x},y={
y}'
print(a) #x = 10,y=20

支持以下這些格式化字符串:


原始字符串(raw strings)

有的時候, 我們需要有 \n 這樣的字符作為轉義字符**. 但是有些時候我們又不希望進行轉義, 只需要原始的**
\n 作為字符串的一部分.

原始字符串中, 轉義字符不生效

例子:QQ發消息時, 有一個 “表情快捷鍵” 的功能. 這個功能就相當於 “轉義字符”.

當開啟了這個功能之後, 在輸入框中輸入 /se 就會被替換成一個表情. 比如我給同事發一個目錄 /search/odin (這本來是表示linux上的一個目錄)


這種情況下, 我們需要關閉 “表情快捷鍵” 功能. 對於Python來說, 我們就可以使用原始字符串來解決這個問題.


  • 在字符串字面值前加上 r或者R 前綴, 表示原始字符串
print(r'hello \n world') #hello \n world

repr函數和反引號操作符

  • 用str函數可以將一些對象轉換成字符串. repr也有類似的效果.
a = 1
print(type(repr(a))) #<class 'str'> 字符串類型
print(str('hello')) # hello
print(repr('hello')) # 'hello'

總結一下, str轉換出的字符串是給人看的. 而repr轉換出的字符串, 是給Python解釋器看的.

  • 意思是說, repr得出的結果, 其實是一個Python語句, 可以直接放到解釋器裡執行~

  • 反引號, 和repr函數等價

string 模塊常用函數

  • Python標准庫提供了string模塊, 包含了很多非常方便實用的函數

注意點:記得字符串是不可變對象, 只能生成新字符串





join函數

將序列中的字符串合並成一個字符串. join函數

a = ['aa','bb','cc']
b = ' '.join(a)
print(b) #aa bb cc

split函數

按空格將字符串分割成列表split函數

a = 'aa bb cc'
b = a.split(' ')
print(b) #['aa', 'bb', 'cc']

通常和join函數一起使用

a = 'aaa,bbb,ccc'
b = a.split(',') #以,分割成列表
print(b)
print(';'.join(b)) #分號連接
print('hello'.join(b))
#執行結果:
['aaa', 'bbb', 'ccc']
aaa;bbb;ccc
aaahellobbbhelloccc

startswith函數 和 endswith函數

判定字符串開頭結尾 startswith函數 和 endswith函數

a = 'hello world'
print(a.startswith('h')) #True
print(a.startswith('hee')) #False
print(a.endswith('d')) #True

strip函數

去除字符串開頭結尾的空格/制表符 strip函數

空白字符:空格,換行,tab

a = ' hello world '
print(a.strip()) #hello world

去掉左側的空白字符:lstrip

去掉右側的空白字符: rstrip

a =' hello \n'
print(f'[{
a.lstrip()}]') #為了方便看,加上[]
print(f'[{
a.rstrip()}]')
print(f'[{
a.strip()}]')
#執行結果:
[hello
]
[ hello]
[hello]

ljust rjust center函數

左對齊/右對齊/中間對齊 ljust rjust center函數

a = ' hello world'
print(a.ljust(30))
print(a.rjust(30))
print(a.center(30))
#執行結果:
hello world
hello world
hello world
find函數

查找子串 find函數

a = ' hello world'
print(a.find('hello')) #4
a = 'hello hello '
print(a.find('h')) #0

返回第一次出現的下標

in差不多,in返回的是布爾值

a = ' hello world'
print(a.find('hello')) #4
print('hello' in a) #True

replace函數

替換子串(記得字符串是不可變對象, 只能生成新字符串). replace函數

a= 'hello world'
print(a.replace('world','python')) #hello python
print(a) #hello world 字符串是不可變對象, 只能生成新字符串

isalpha函數 和 isdigit函數

判定字符串是字母/數字 isalpha函數 和 isdigit函數

a = 'hello 1'
print(a.isalpha()) #False
a = '1234'
print(a.isdigit()) #True

lower和upper函數

轉換大小寫 lower和upper函數

a = 'Hello world'
print(a.lower()) #hello world
print(a.upper()) #HELLO WORLD

關於結束符

學過C語言的同學, 可能會問, Python的字符串是否需要 ‘\0’ 之類的結束符來做結尾?

  • Python中並沒有那個討厭的 ‘\0’. 准確的說, 對於C語言來說, 語言本身的缺陷並不支持 “字符串類型”, 才被迫使用字符數組來湊合. 但是Python完全沒有這個包袱.

列表 []

字符串只能由字符組成, 而且不可變; 但是列表中可以包含任意類型的對象, 使用更加靈活.

使用切片操作符訪問列表元素

  • 列表的切片操作和字符串完全一致.
  • 但是列表還可以使用切片操作修改元素.
a = [1,2,3,4]
a[0] = 100
print(a) #[100,2,3,4]

列表常用操作

append: 追加元素

把append的東西當成一個元素

a = [1,2,3]
a.append('a')
print(a) #[1,2,3,'a']

注意:使用append:是把元素當成整體插入

a = [1,2]
a.append([3,4]) #插入的是個整體
print(a) #[1,2,[3,4]] 二維列表
print(len(a)) #3

刪除指定下標元素 del

a = [1,2,3]
del(a[0])
print(a) #[2,3]

按值刪除元素 remove

a = [1,2,3]
a.remove(1)
print(a) #[2, 3]
#如果元素不存在,就會報錯
a.remove(4) #報錯 ValueError: list.remove(x): x not in list

列表比較操作

== != 為判定所有元素都相等, 則認為列表相等;

< <= > >= 則是兩個列表從第一個元素開始 依次比較, 直到某一方勝出. 按順序比較

a = ['abc',12]
b = ['acb',123]
c = ['abc',11]
print(a<c) #False
print(b<c) #False
print(b >c and a > c) #True
a = [1,2,3]
b = [3,2,1]
print(a <b) #True 按順序比
a = [1,2,3]
b = ['a','b']
print(a<b) #報錯:TypeError: '<' not supported between instances of 'int' and 'str'

in/not in: 判斷一個元素在不在列表中

時間復雜度O(N)

a = [1,2,3,'a']
print('a' in a) #True
print(4 in a) #False

連接操作符(+): 連接兩個列表

a和b都不變,生成新對象

a = [1,'a']
b = [2,'b']
print(a+b) #[1, 'a', 2, 'b']

extend: 列表連接

如果使用append:是把元素當成整體插入

a = [1,2]
a.append([3,4]) #插入的是個整體
print(a) #[1,2,[3,4]] 
print(len(a)) #3

使用extend

可以理解成 += (列表不支持 += 運算符)

a = [1,2]
a.extend([3,4])
print(a) #[1,2,3,4]
print(len(a)) #4

重復操作符(*):

a = [1,2]
print(a*3) #[1, 2, 1, 2, 1, 2]

insert:任意位置插入

如果下標很大,插入到最後面

insert沒有返回值:

a =[1,2,3]
print(a.insert(0,0)) #None

例子:

a=[1,2,3]
a.insert(0,0) #在0下標位置插入0
a.insert(100,100) #在下標100位置插入100
print(a) #[0, 1, 2, 3, 100]

reverse:原地翻轉列表

a = [1,2,3,'ac']
a.reverse()
print(a) #['ac', 3, 2, 1]

也可以通過切片翻轉 a[::-1]和reverse翻轉的區別:

切片翻轉不修改原來對象,生成新對象, reverse翻轉修改原來對象


sort:原地排序

這個是列表內置的成員函數,和sorted不同

例如, 排序. 我們前面講序列的時候, 有一個內建函數sorted, sorted生成了一個新的有序的序列. 列表自身還有一個成員函數sort, 是基於自己本身做修改, 並不生成新對象.

a = [4,3,2,1]
sorted(a)#內建函數sorted
print(a)#[4, 3, 2, 1] a不改變
a.sort()#成員函數sort
print(a)#[1, 2, 3, 4]
a.sort(reverse=True) #翻轉
print(a) #[4,3,2,1]

關於sort方法, 默認使用歸並排序的衍生算法. 時間復雜度是 O(N*log(N))


count:統計元素出現次數

a = [4,3,2,1,2,4,2]
print(a.count(2)) # 3
print(a.count(5)) # 0

index:相當於查找,返回下標

a = [4,3,2,1]
print(a.index(4)) #0
print(a.index(5)) #報錯 ValueError: 5 is not in list

pop:刪除列表一個元素

下標刪除,默認值是-1,刪最後一個元素

如果列表為空還彈出,就會報錯

a =[1]
print(a) #[1]
a.pop()
print(a) #[]
a.pop() #列表為空還彈出,報錯:IndexError: pop from empty list

還可以給下標刪除:如果下標越界會報錯

a = [1,2,3,4]
a.pop(3) #刪除下標為3的元素
print(a) #[1,2,3]
a.pop(6) #報錯 IndexError: pop index out of range

以上函數注意點:

列表自身是可變對象, 因此有些方法是修改自身的, 這種情況下的方法並沒有返回值,

這和字符串操作必須要生成一個新對象, 並不相同.


基於列表的堆棧

我們回顧一下數據結構中的堆棧, 這是一種後進先出的數據結構. 可以很容易使用列表進行模擬

a = [] #空列表
a.append(1) #push操作 在棧頂插入元素
print(a[-1]) #top操作 得到棧頂元素
a.pop() #pop操作 彈出棧頂元素

基於列表的隊列

我們回顧一下數據結構中的隊列, 這是一種先進先出的數據結構. 也可以很容易使用列表模擬

a = [] #空列表
a.append(1) #push操作 在隊尾插入元素
print(a[0]) #top操作 得到隊頭元素
a.pop(0) #pop操作 彈出隊頭元素

列表的深拷貝/淺拷貝(選學)

#定義一個列表對象a.
a = [100, [1, 2]]
#基於a, 用三種方式創建了b, c, d
b = a
c = list(a)
d = a[:]
print(id(a), id(b), id(c), id(d)) #a和b同 ,c,d不同 b和a實際上是同一個對象. 而c和d都已經是新的列表對象了.
a[0] = 1
print(a, b, c, d) #a和b受影響 c和d不受影響
a[1][0] = 1000
print(a, b, c, d) #四個都受影響
print(id(a[1]), id(c[1]), id(d[1])) #三者相同
# 執行結果
140541879626136 140541879626136 140541879655672 140541879655744
[1, [1, 2]] [1, [1, 2]] [100, [1, 2]] [100, [1, 2]]
[1, [1000, 2]] [1, [1000, 2]] [100, [1000, 2]] [100, [1000, 2]]
139973004362816 139973004362816 139973004362816

結果稍微有點復雜, 我們一點一點分析

  • 定義一個列表對象a.
  • 基於a, 用三種方式創建了b, c, d
  • 首先可以看到, b和a實際上是同一個對象. 而c和d都已經是新的列表對象了.
  • 修改 a[0] 可以看到, a和b的值都收到了影響. 但是c和d不受影響, 因為a和b是同一個對象 c和d是新對象
  • 再修改 a[1][0]=1000 這時候驚奇的發現, c和d的內容也收到影響了!!!
  • 這時候我們猜測, a, c, d的下標為1的列表元素, 其實都是同一個對象. 打印出id, 證明了我們的猜測.


總結:

  • 列表賦值(=)沒有創建新對象, 但是list工廠函數和切片操作都創建了新的列表對象.
  • 列表的賦值(=), 切片操作([:]), list工廠函數, 對象內部元素 都是在進行淺拷貝.
  • 淺拷貝是指, 雖然我重新創建了一個新對象, 但是新對象裡面包含的子對象, 和原來的子對象是同一個對象.
  • 深拷貝就是指, 重新創建了一個新對象, 同時這個新對象的子對象, 也都是重新創建了一份新的深
    拷貝對象.
  • 深拷貝通過 copy.deepcopy 來完成

如何進行深拷貝:

注意:深拷貝 只能針對可變對象使用

進行深拷貝: 需要導入copy模塊

import copy
a = [100, [1, 2]]
b = copy.deepcopy(a)
print(id(a[1]), id(b[1]) )
# 執行結果
139818205071552 139818205117400

這次我們看到, a[1] 和 b[1] 不再是一個對象了.

  • 不光是列表, 其他容器, 例如元組, 字典, 也存在類似的深淺拷貝問題. 原理和列表相同
  • 需要注意的是, 並不一定調用了deepcopy就一定會執行深拷貝, Python解釋器自己也會根據具體的情況,比如某些不適合深拷貝的情況, 不會觸發深拷貝.

import copy
a = (100, 'aaa')
b = copy.deepcopy(a)
print(id(a[0]), id(a[1]), id(b[0]), id(b[1]))
# 執行結果
6783664 140700444542616 6783664 140700444542616

想想這是為啥?

進行深拷貝的目的:防止其中一份數據改了,會影響其它數據的結果

深拷貝就是為了改一個另外一個不變,但如果是不可變對象,都不允許修改,需求都不存在,

因為元組的內容本身就不能修改,沒必要進行深拷貝!


元組 ()

元組的很多操作都和列表一致. 唯一的區別是元組的內容是只讀的.

元組常用操作

切片操作: 和列表相同

比較運算符, 規則和列表相同

連接操作(+), 重復操作(*), 判定元素存在(in/not in)都和列表相同.

**由於元組不可變, 所以並不支持append, extend, sort,[],等修改自身的操作.**當然了,也不支持深拷貝


默認集合類型

  • 所有的多個對象, 按逗號分隔的, 其實都是元組

回憶我們之前那個牛逼閃閃的交換兩個元素的值的代碼.

x, y = y, x

其實, x, y 就是一個元組. 通過 y, x 這個元組創建了一個新的元組

x = 1
y = 2
x,y = y,x
print(type((x,y))) #<class 'tuple'>

理解元組的 “不可變”

  • 元組的 “不可變” 指的是元組的每個元素的id不可變. 就是說一個元組包含了幾個對象, 然後不可以給這個元組再添加或者刪除其中的某個對象, 也不可以將某個對象改成其他的對象.
  • 如果元組中的某個元素, 是可變對象(比如列表或字典), 那麼仍然是可以修改的.
a = ([1,2],[3,4])
a[0][0] = 100
print(a) #([100, 2], [3, 4])

元組:用逗號分開元素

a = (10) #因為可能是(10+2)*2這種表達式,所以a是整數,不是元組
print(type(a)) #<class 'int'>
a = (10,2)
print(type(a)) #<class 'tuple'>

注意:

此處用的是元組重復操作符(*)的概念.

a = (10+20,) *3 #此處沒有逗號,a就是90
print(a) #(30, 30, 30)
print(type(a)) #<class 'tuple'> 元組

“不可變” 真的有必要嘛? 假如我們只是用列表, 能否應付所有的應用場景呢?

答案是否定的. 既然Python的設計者提供了元組這個對象, 那麼一定有一些情況下, 只有元組能勝任, 但是列表無法勝任.

元組不可以替代的原因

原因1:函數傳參的時候使用元組可以避免函數內部把函數外部的內容修改掉

你有一個列表, 現在需要調用一個API進行一些處理. 但是你有不是特別確認這個API是否會把你的列表數據弄亂. 那麼這時候傳一個元組就安全很多.

a = [1,2,3]
def func(input_list):
input_list[0] = 100
func(a)
print(a) #[100,2,3] 列表可以修改
#如果用的是元組:
a = (1,2,3)
def func(input_list):
input_list[0] = 100
func(a) #報錯:TypeError: 'tuple' object does not support item assignment
print(a)

原因2:元組是可哈希的,元組就可以作為字典的key,而列表是可變的,列表不可以作為字典的key

我們馬上要講的字典, 是一個鍵值對結構. 要求字典的鍵必須是 “可hash對象” (字典本質上也是一個hash表). 而一個可hash對象的前提就是不可變. 因此元組可以作為字典的鍵, 但是列表不行.

a = (1,2,3)
b = {

a:100
}
print(b) # {(1, 2, 3): 100}
#如果是列表:報錯
a = [1,2,3]
b = {

a:100 #TypeError: unhashable type: 'list'
}
print(b)

字典{}

Python的字典是一種映射類型數據. 裡面的數據是 鍵值對 . Python的字典是基於hash表實現的.

  • 鍵(key): 字典的鍵是唯一的(一個字典對象中不能有兩個相同的鍵). 同時字典的鍵必須是可hash的(能夠計算出一個固定的hash值)
  • 值(value): 字典的值沒啥限制. 數字, 字符串, 以及其他容器, 都可以作為字典的值

注意:字典的鍵和值的類型, 不需要相同

不同的鍵值用逗號分割

a = {

10:20,
'key':'value',
'a':True
}
print(a) #{10: 20, 'key': 'value', 'a': True}

字典的key必須是可哈希的,不可變是可hash的必要條件

  • 可以使用hash()函數判斷是否可以哈希
print(hash((10,20))) #元組可以哈希 3713074054217192181
print(hash([1,2])) #列表不可以哈希 報錯:TypeError: unhashable type: 'list'

創建字典

  • 使用 { } 的方法
a = {
'name':"Mango",'age':20}
print(a) #{'name': 'Mango', 'age': 20}
  • 使用工廠方法 dict

    元組作為參數,元組的每個參數是個列表,列表裡面有兩個值: 一個是鍵 一個是值

a = dict((['name',"Mango"],['age',20]))
print(a) #{'name': 'Mango', 'age': 20}

  • 使用字典的內建方法 fromkeys
a = {
}.fromkeys(('name',"Ename"),"Mango")
print(a) #{'name': 'Mango', 'Ename': 'Mango'}
a = {
}.fromkeys(('name'),"Mango")
print(a) #{'n': 'Mango', 'a': 'Mango', 'm': 'Mango', 'e': 'Mango'}

注意:字典的鍵和值的類型,不需要相同.


訪問字典中的元素

  • 使用 [] 可以獲取到元素的值.
    • 注意:鍵是字符串,所以訪問的是也要是字符串形式
a = {
'name':"Mango",'age':20}
print(a['name']) #Mango
print(a['age']) #20
  • for循環遍歷字典中的元素
a = {
'name':"Mango",'age':20}
for item in a: #item相當於每個鍵
print(item,a[item]) #打印鍵 和 值
#執行結果:
name Mango
age 20

注意:字典中的鍵值對, 順序是不確定的(回憶hash表的數據結構, 並不要求key有序).


修改字典元素

使用 [] 可以新增/修改字典元素. -> 如果key不存在, 就會新增; 如果已經存在, 就會修改.

a = {
} #空字典
a['name'] ="Mango"
print(a) #{'name': 'Mango'}
a['name'] = "Lemon"
print(a) #{'name': 'Lemon'}

刪除字典元素

  • 使用del來刪除某一個鍵值對
a = {
'name':"Mango",'age':20}
del a['name']
print(a) #{'age': 20}
  • 使用clear方法, 清空整個字典中所有的鍵值對
a = {
'name':"Mango",'age':20}
a.clear()
print(a) #{} 空字典
  • 使用pop函數, 刪除鍵值對, 同時獲取到值
a = {
'name':"Mango",'age':20}
b = a.pop('name')
print(a) # {'age': 20}
print(b) # Mango

注意:字典也是可變對象. 但是鍵值對的key是不能修改的



常用內置函數

in / not in:

**判定一個key是否在字典中 ** (判斷的是key,不是value)

a = {
'name':"Mango",'age':20}
print('name' in a) #True
print('Mango' in a) #False

len函數

len: 字典中鍵值對的數目

a = {
'name':"Mango",'age':20}
print(len(a)) #2

hash函數

hash: 判定一個對象是否可hash. 如果可hash, 則返回hash之後的hashcode. 否則會運行出錯.

print(hash(())) #3527539 元組不可變,可以哈希
print(hash([])) #TypeError: unhashable type: 'list' 列表可變,不可哈希

keys

返回一個列表, 包含字典的所有的key

a = {
'name':"Mango",'age':20}
print(a.keys()) #dict_keys(['name', 'age'])

values

返回一個列表, 包含字典的所有value

a = {
'name':"Mango",'age':20}
print(a.values()) #dict_values(['Mango', 20])

items

返回一個列表, 每一個元素都是一個元組, 包含了key和value

a = {
'name':"Mango",'age':20}
print(a.items()) #dict_items([('name', 'Mango'), ('age', 20)]) 

集合(set)

集合是基於字典實現的一個數據結構. 集合數據結構實現了很多數學中的交集, 並集, 差集等方法, 可以很方便的進行這些操作.

注意!! 熟練掌握集合操作, 對我們後續解筆試題面試題, 都會有很大幫助.


集合基本操作

集合和列表不同,集合的順序沒有影響

a = set([1,2,3])
b = set([3,2,1])
print(a == b) #True
print(a,b) #{1, 2, 3} {1, 2, 3}

取交集& 並集| 差集 - 對稱差集^

a = set([1, 2, 3])
b = set([1, 2, 3, 4])
print(a & b) # 取交集
print(a | b) # 取並集
print(b - a) # 取差集
print(a ^ b) # 取對稱差集(項在a中或者在b中, 但是不會同時存在於a和b中)
#執行結果
{
1, 2, 3}
{
1, 2, 3, 4}
{
4}
{
4}

數據去重 使用->set

a =set( [1, 1, 2, 2, 3, 3])
print(a) #{1,2,3}
a = [1,2,1,11,1,1,2]
b = set(a)
print(a) #[1, 2, 1, 11, 1, 1, 2] 
print(b) #{1, 2, 11}

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved