1. 程式人生 > >python之資料型別補充、集合、深淺copy

python之資料型別補充、集合、深淺copy


一、內容回顧
程式碼塊: 一個函式,一個模組,一個類,一個檔案,互動模式下,每一行就是一個程式碼塊。
is == id
id()查詢物件的記憶體地址
== 比較的是兩邊的數值。
is 比較的是兩邊的記憶體地址。
小資料池:
前提:int,str,bool
1,節省記憶體。
2,提高效能和效率。
小資料池是什麼?
在記憶體中,建立一個'池',提前存放了 -5 ~256 的整數,一定規則的字串和bool值。
後續程式中,如果設定的變數指向的是小資料池的內容,那麼就不會再記憶體中重新建立。


小資料池與程式碼塊的關係。
同一個程式碼塊:python在執行時,遇到了初始化物件命令,他會將這個變數名和數值放到一個字典中,


再次遇到他會從這字典中尋找。
不同程式碼塊:python在執行時,直接從小資料池中尋找,滿足條件id相同。
編碼:
python3x:

英文:
str: 表現形式:s1 = 'hello'
內部編碼方式: unicode

bytes:表現形式:s1 = b'hello'
內部編碼方式: 非unicode
中文:
str: 表現形式:s1 = '小白'
內部編碼方式: unicode

bytes:表現形式:s1 = b'\xe2\xe2\xe2\xe2\xe2\xe2'
內部編碼方式: 非unicode
只有當你想要儲存一些內容到檔案中,或者通過網路傳輸時,才要用的bytes型別


str --->bytes: encode
bytes--->str: decode




補充:

複製程式碼
s1 = '小黑'
b1 = s1.encode('gbk')
print(b1)            #gbk的bytes型別

# gbk的bytes型別 -----> utf-8的bytes型別,正常情況是這樣轉換:
s2 = b1.decode('gbk')     # 先按照對應的編碼方式 解碼成字串(unicode)
b2 = s2.encode('utf-8')    # 再編碼成utf-8的bytes
print
(b2)
複製程式碼

 

非中文的字串還可以這樣解碼:

複製程式碼
s1 = 'xiaoming'
b1 = s1.encode('gbk')     #gbk的bytes型別

s2 = b1.decode('utf-8')    #可以按照utf-8的形式解碼
print(s2)

# 上面程式碼能成立:因為utf-8 gbk,unicode等編碼的英文字母,數字,特殊字元都是對映的ASCII碼。
複製程式碼

 

 

二、基礎資料型別補充
1、元組:
如果元組中只有一個數據,且沒有逗號,那麼該"元組"的資料型別與裡面的資料型別一致
否則,該資料型別就是元組

複製程式碼
tu1 = (1)
print(tu1,type(tu1))  # 1 <class 'int'>

tu1 = (1,)
print(tu1,type(tu1))  # (1,) <class 'tuple'>

tu2 = ('hello')
print(tu2,type(tu2))  # hello <class 'str'>

tu2 = ('hello',)
print(tu2,type(tu2))  # ('hello',) <class 'tuple'>
複製程式碼

 


2、列表:
列表與列表可以相加(就是拼接)
l1 = [1,2,3]
l2 = ['aa','bb']
l3 = l1 + l2
print(l3) --->[1, 2, 3, 'aa', 'bb']


li = [11, 22, 33, 44, 55, 66, 77, 88]
將列表中索引為奇數的元素,全部刪除.
也許剛接觸的時候會有人這麼寫:

複製程式碼
li = [11, 22, 33, 44, 55, 66, 77, 88]
# 問題程式碼1:
for i in li:
    if li.index(i) % 2 == 1:
        li.remove(i)
print(li)


# 問題程式碼2:
for i in range(len(li)):
    if i % 2 == 1:
        li.pop(i)
print(li)
複製程式碼

 

但是你會發現這樣做並不能實現結果,要麼報錯,要麼實現不了預想的結果,為什麼呢?
這是因為:在迴圈一個列表時,如果對列表中的某些元素進行刪除,
那麼此元素後面的所有元素就會向前進一位,他們的索引和長度就會發生變化。

所以正確的方法可以這樣寫:

複製程式碼
li = [11, 22, 33, 44, 55, 66, 77, 88]
# 方法一:切片+步長刪除
del li[1::2]
print(li)


# 方法二:
l2 = []
for i in range(len(li)):
    if i % 2 == 0:
        l2.append(li[i])
li = l2
print(li)

# 方法三:倒著刪除
for index in range(len(li)-1, -1, -1):
    if index % 2 == 1:
        li.pop(index)
print(li)
複製程式碼

總結:在迴圈一個列表時,最好不要對此列表進行改變大小(增刪)的操作。


3、字典:
建立字典的方式:
(1)直接建立:dic = {'name':'hello','age':18}
(2)dic = dict({'name':'hello','age':18})
(3)dic = dict.fromkeys([1,2,3],'hello')      #迭代建立(第一個引數是可迭代物件,str list dict等)
結果: {1: 'hello', 2: 'hello', 3: 'hello'}

陷阱:
(1)
dic = dict.fromkeys([1,2,3],'hello')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:

{1: 'hello', 2: 'hello', 3: 'hello'}
1604999043984
1604999043984
1604999043984


(2)
dic = dict.fromkeys([1,2,3],[])
print(dic)
這樣建立的是值為空列表的字典:
{1: [], 2: [], 3: []}


dic[1].append('nihao')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:
{1: ['nihao'], 2: ['nihao'], 3: ['nihao']}
2347486287880
2347486287880
2347486287880

 

dic[2].append('I am fine')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:
{1: ['nihao', 'I am fine'], 2: ['nihao', 'I am fine'], 3: ['nihao', 'I am fine']}

2347486287880
2347486287880
2347486287880

結論:dict.fromkeys()方法迭代建立的字典,迭代的鍵都是指向同一個記憶體地址(值相同)

 

 

 

 

複製程式碼
dic = {'key1': 'value1', 'key2': 'value2', 'k3': 'v3', 'name': 'aaa'}
# 將dic的鍵中含有k元素的所有鍵值對刪除。
# 錯誤程式碼:
for key in dic:
    if 'k' in key:
        dic.pop(key)
print(dic)
# 這樣寫會報錯dictionarychangedsizeduringiteration
# 這是因為在迴圈一個字典時,不能改變字典的大小,否則就會報錯。



# 正確方法可以:
l1 = []
for key in dic:
    if 'k' in key:
        l1.append(key)
for key in l1:  # 第二次迴圈的是含有'k'的所有鍵組成的一個列表,並在迴圈列表的時候刪除字典裡的鍵值對
    dic.pop(key)
print(dic)
複製程式碼

 


資料型別的轉換。
'''
int str bool 三者轉換
str <---> bytes
str <---> list
dict.keys() dict.values() dict.items() list()
tuple <---> list
dict ---> list
'''

str ---> list:split()
s1 = 'aa bb cc'
l1 = s1.split()
print(l1)

list ---> str:join() 此list中的元素全部是str型別才可以轉換
l1 = ['aa', 'bb', 'cc']
s2 = ' '.join(l1)
print(s2)

list ---> tuple
l1 = [1,2,3]
tu1 = tuple(l1)
print(tu1)

tuple ---> list
tu2 = (0,2,3)
l1 = list(tu2)
print(l1)


dict ---> list
dic1 = {'name': 'alex', 'age': 1000}
l1 = list(dic1)
l2 = list(dic1.keys())
l3 = list(dic1.values())
l4 = list(dic1.items())
print(l1)
print(l2)
print(l3)
print(l4)

 


三、集合set
set:
{'aa','bb',1,2,3}
集合要求裡面的元素必須是不可變的資料型別但是集合本身是可變的資料型別。
集合裡面的元素不重複(天然去重),無序
主要用途:
1,去重。
2,關係測試。

set1 = {'abc', [1,2], 1,2,3}     # 這個是錯誤的   因為集合要求裡面的元素必須是不可變的資料型別,因此這裡會報錯(列表是可變的資料型別)
set2 = {'aa', 'bb'}           #直接定義
set3 = set({'aa', 'bb'})    #set()方法
print(set2)

list去重 *****
l1 = [1,1,2,3,4,4,3,2,1,5,5]
set1 = set(l1)        #先把列表轉換成集合,自動去重
l2 = list(set1)        #再把集合轉換成列表
print(l2)


set1 = {'hello','handsome','boy','you','good'}

set1.add('女神')
print(set1)

set1.update('abc')    #update:迭代著增加
print(set1)


set1.remove('hello') # 刪除一個元素
print(set1)

set1.pop() # 隨機刪除一個元素
print(set1)

set1.clear() # 清空集合
print(set1)

del set1 # 刪除集合
print(set1)

關係測試

set1 = {1,2,3,4,5}
set2 = {4,5,6,7,8}

交集(& 或者 intersection)
print(set1 & set2)                    # {4, 5}
print(set1.intersection(set2))   # {4, 5}

並集(| 或者 union)
print(set1 | set2)              #{1, 2, 3, 4, 5, 6, 7, 8}
print(set1.union(set2))

反交集(^ 或者 symmetric_difference)
print(set1 ^ set2)                  # {1, 2, 3, 6, 7, 8}
print(set1.symmetric_difference(set2))

差集(- 或者 difference)
print(set1 - set2)            # {1, 2, 3}
print(set1.difference(set2))
print(set2 - set1)           #{8, 6, 7}

子集
set1 = {1,2,3}
set2 = {1,2,3,4,5,6}
print(set1 < set2)      # True
print(set1.issubset(set2))

超集
print(set2 > set1)
print(set2.issuperset(set1))

frozenset不可變集合,讓集合變成不可變型別。
set1 = {1,2,3}
set2 = frozenset(set1)
print(set2)                 # 不可變的資料型別。 ***

 

 

四、深淺copy
賦值運算
l1 = [1,2,3]
l2 = l1
l1.append(666)
print(l2)
print(id(l1))
print(id(l2))    #是同一個地址,l2 = l1只是把l2指向了l1的地址,l1改變,l2也改變

 

淺copy(只針對列表,字典,集合):資料(列表)第二層開始可以與原資料進行公用
深copy(引用copy模組,任意資料型別都可深copy):完全獨立的copy一份資料,與原資料沒有關係

淺copy
l1 = [1,2,3]
l2 = l1.copy()
l1.append(666)
print(l2)                #第一層是獨立的:[1, 2, 3]

 

l1 = [1,2,3,[22,]]
l2 = l1.copy()
l1[-1].append(666)
print(l1,l2)       #第二層開始與原資料公用:[1, 2, 3, [22, 666]] [1, 2, 3, [22, 666]]

print(id(l1))   #2357463048008
print(id(l2))  # 2357463013768
print(id(l1[-1]))  # 2357463047816
print(id(l2[-1]))  # 2357463047816

 

 

 

 


深copy
import copy
l1 = [1,2,3,[22,]]
l2 = copy.deepcopy(l1)
print(l1,l2)   # [1, 2, 3, [22]]    [1, 2, 3, [22]]
l1[-1].append(666)  
print(l1)   # [1, 2, 3, [22, 666]]

print(l2)   # [1, 2, 3, [22]]

 

 

 

 


切片屬於淺copy
l1 = [1,2,3,[22,33]]
l2 = l1[:]
# l1.append(666)
l1[-1].append(666)
print(l2)  # [1, 2, 3, [22, 33, 666]]

 


一、內容回顧
程式碼塊: 一個函式,一個模組,一個類,一個檔案,互動模式下,每一行就是一個程式碼塊。
is == id
id()查詢物件的記憶體地址
== 比較的是兩邊的數值。
is 比較的是兩邊的記憶體地址。
小資料池:
前提:int,str,bool
1,節省記憶體。
2,提高效能和效率。
小資料池是什麼?
在記憶體中,建立一個'池',提前存放了 -5 ~256 的整數,一定規則的字串和bool值。
後續程式中,如果設定的變數指向的是小資料池的內容,那麼就不會再記憶體中重新建立。


小資料池與程式碼塊的關係。
同一個程式碼塊:python在執行時,遇到了初始化物件命令,他會將這個變數名和數值放到一個字典中,
再次遇到他會從這字典中尋找。
不同程式碼塊:python在執行時,直接從小資料池中尋找,滿足條件id相同。
編碼:
python3x:

英文:
str: 表現形式:s1 = 'hello'
內部編碼方式: unicode

bytes:表現形式:s1 = b'hello'
內部編碼方式: 非unicode
中文:
str: 表現形式:s1 = '小白'
內部編碼方式: unicode

bytes:表現形式:s1 = b'\xe2\xe2\xe2\xe2\xe2\xe2'
內部編碼方式: 非unicode
只有當你想要儲存一些內容到檔案中,或者通過網路傳輸時,才要用的bytes型別


str --->bytes: encode
bytes--->str: decode




補充:

複製程式碼
s1 = '小黑'
b1 = s1.encode('gbk')
print(b1)            #gbk的bytes型別

# gbk的bytes型別 -----> utf-8的bytes型別,正常情況是這樣轉換:
s2 = b1.decode('gbk')     # 先按照對應的編碼方式 解碼成字串(unicode)
b2 = s2.encode('utf-8')    # 再編碼成utf-8的bytes
print(b2)
複製程式碼

 

非中文的字串還可以這樣解碼:

複製程式碼
s1 = 'xiaoming'
b1 = s1.encode('gbk')     #gbk的bytes型別

s2 = b1.decode('utf-8')    #可以按照utf-8的形式解碼
print(s2)

# 上面程式碼能成立:因為utf-8 gbk,unicode等編碼的英文字母,數字,特殊字元都是對映的ASCII碼。
複製程式碼

 

 

二、基礎資料型別補充
1、元組:
如果元組中只有一個數據,且沒有逗號,那麼該"元組"的資料型別與裡面的資料型別一致
否則,該資料型別就是元組

複製程式碼
tu1 = (1)
print(tu1,type(tu1))  # 1 <class 'int'>

tu1 = (1,)
print(tu1,type(tu1))  # (1,) <class 'tuple'>

tu2 = ('hello')
print(tu2,type(tu2))  # hello <class 'str'>

tu2 = ('hello',)
print(tu2,type(tu2))  # ('hello',) <class 'tuple'>
複製程式碼

 


2、列表:
列表與列表可以相加(就是拼接)
l1 = [1,2,3]
l2 = ['aa','bb']
l3 = l1 + l2
print(l3) --->[1, 2, 3, 'aa', 'bb']


li = [11, 22, 33, 44, 55, 66, 77, 88]
將列表中索引為奇數的元素,全部刪除.
也許剛接觸的時候會有人這麼寫:

複製程式碼
li = [11, 22, 33, 44, 55, 66, 77, 88]
# 問題程式碼1:
for i in li:
    if li.index(i) % 2 == 1:
        li.remove(i)
print(li)


# 問題程式碼2:
for i in range(len(li)):
    if i % 2 == 1:
        li.pop(i)
print(li)
複製程式碼

 

但是你會發現這樣做並不能實現結果,要麼報錯,要麼實現不了預想的結果,為什麼呢?
這是因為:在迴圈一個列表時,如果對列表中的某些元素進行刪除,
那麼此元素後面的所有元素就會向前進一位,他們的索引和長度就會發生變化。

所以正確的方法可以這樣寫:

複製程式碼
li = [11, 22, 33, 44, 55, 66, 77, 88]
# 方法一:切片+步長刪除
del li[1::2]
print(li)


# 方法二:
l2 = []
for i in range(len(li)):
    if i % 2 == 0:
        l2.append(li[i])
li = l2
print(li)

# 方法三:倒著刪除
for index in range(len(li)-1, -1, -1):
    if index % 2 == 1:
        li.pop(index)
print(li)
複製程式碼

總結:在迴圈一個列表時,最好不要對此列表進行改變大小(增刪)的操作。


3、字典:
建立字典的方式:
(1)直接建立:dic = {'name':'hello','age':18}
(2)dic = dict({'name':'hello','age':18})
(3)dic = dict.fromkeys([1,2,3],'hello')      #迭代建立(第一個引數是可迭代物件,str list dict等)
結果: {1: 'hello', 2: 'hello', 3: 'hello'}

陷阱:
(1)
dic = dict.fromkeys([1,2,3],'hello')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:

{1: 'hello', 2: 'hello', 3: 'hello'}
1604999043984
1604999043984
1604999043984


(2)
dic = dict.fromkeys([1,2,3],[])
print(dic)
這樣建立的是值為空列表的字典:
{1: [], 2: [], 3: []}


dic[1].append('nihao')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:
{1: ['nihao'], 2: ['nihao'], 3: ['nihao']}
2347486287880
2347486287880
2347486287880

 

dic[2].append('I am fine')
print(dic)
print(id(dic[1]))
print(id(dic[2]))
print(id(dic[3]))
結果:
{1: ['nihao', 'I am fine'], 2: ['nihao', 'I am fine'], 3: ['nihao', 'I am fine']}

2347486287880
2347486287880
2347486287880

結論:dict.fromkeys()方法迭代建立的字典,迭代的鍵都是指向同一個記憶體地址(值相同)

 

 

 

 

複製程式碼
dic = {'key1': 'value1', 'key2': 'value2', 'k3': 'v3', 'name': 'aaa'}
# 將dic的鍵中含有k元素的所有鍵值對刪除。
# 錯誤程式碼:
for key in dic:
    if 'k' in key:
        dic.pop(key)
print(dic)
# 這樣寫會報錯dictionarychangedsizeduringiteration
# 這是因為在迴圈一個字典時,不能改變字典的大小,否則就會報錯。



# 正確方法可以:
l1 = []
for key in dic:
    if 'k' in key:
        l1.append(key)
for key in l1:  # 第二次迴圈的是含有'k'的所有鍵組成的一個列表,並在迴圈列表的時候刪除字典裡的鍵值對
    dic.pop(key)
print(dic)
複製程式碼

 


資料型別的轉換。
'''
int str bool 三者轉換
str <---> bytes
str <---> list
dict.keys() dict.values() dict.items() list()
tuple <---> list
dict ---> list
'''

str ---> list:split()
s1 = 'aa bb cc'
l1 = s1.split()
print(l1)

list ---> str:join() 此list中的元素全部是str型別才可以轉換
l1 = ['aa', 'bb', 'cc']
s2 = ' '.join(l1)
print(s2)

list ---> tuple
l1 = [1,2,3]
tu1 = tuple(l1)
print(tu1)

tuple ---> list
tu2 = (0,2,3)
l1 = list(tu2)
print(l1)


dict ---> list
dic1 = {'name': 'alex', 'age': 1000}
l1 = list(dic1)
l2 = list(dic1.keys())
l3 = list(dic1.values())
l4 = list(dic1.items())
print(l1)
print(l2)
print(l3)
print(l4)

 


三、集合set
set:
{'aa','bb',1,2,3}
集合要求裡面的元素必須是不可變的資料型別但是集合本身是可變的資料型別。
集合裡面的元素不重複(天然去重),無序
主要用途:
1,去重。
2,關係測試。

set1 = {'abc', [1,2], 1,2,3}     # 這個是錯誤的   因為集合要求裡面的元素必須是不可變的資料型別,因此這裡會報錯(列表是可變的資料型別)
set2 = {'aa', 'bb'}           #直接定義
set3 = set({'aa', 'bb'})    #set()方法
print(set2)

list去重 *****
l1 = [1,1,2,3,4,4,3,2,1,5,5]
set1 = set(l1)        #先把列表轉換成集合,自動去重
l2 = list(set1)        #再把集合轉換成列表
print(l2)


set1 = {'hello','handsome','boy','you','good'}

set1.add('女神')
print(set1)

set1.update('abc')    #update:迭代著增加
print(set1)


set1.remove('hello') # 刪除一個元素
print(set1)

set1.pop() # 隨機刪除一個元素
print(set1)

set1.clear() # 清空集合
print(set1)

del set1 # 刪除集合
print(set1)

關係測試

set1 = {1,2,3,4,5}
set2 = {4,5,6,7,8}

交集(& 或者 intersection)
print(set1 & set2)                    # {4, 5}
print(set1.intersection(set2))   # {4, 5}

並集(| 或者 union)
print(set1 | set2)              #{1, 2, 3, 4, 5, 6, 7, 8}
print(set1.union(set2))

反交集(^ 或者 symmetric_difference)
print(set1 ^ set2)                  # {1, 2, 3, 6, 7, 8}
print(set1.symmetric_difference(set2))

差集(- 或者 difference)
print(set1 - set2)            # {1, 2, 3}
print(set1.difference(set2))
print(set2 - set1)           #{8, 6, 7}

子集
set1 = {1,2,3}
set2 = {1,2,3,4,5,6}
print(set1 < set2)      # True
print(set1.issubset(set2))

超集
print(set2 > set1)
print(set2.issuperset(set1))

frozenset不可變集合,讓集合變成不可變型別。
set1 = {1,2,3}
set2 = frozenset(set1)
print(set2)                 # 不可變的資料型別。 ***

 

 

四、深淺copy
賦值運算
l1 = [1,2,3]
l2 = l1
l1.append(666)
print(l2)
print(id(l1))
print(id(l2))    #是同一個地址,l2 = l1只是把l2指向了l1的地址,l1改變,l2也改變

 

淺copy(只針對列表,字典,集合):資料(列表)第二層開始可以與原資料進行公用
深copy(引用copy模組,任意資料型別都可深copy):完全獨立的copy一份資料,與原資料沒有關係

淺copy
l1 = [1,2,3]
l2 = l1.copy()
l1.append(666)
print(l2)                #第一層是獨立的:[1, 2, 3]

 

l1 = [1,2,3,[22,]]
l2 = l1.copy()
l1[-1].append(666)
print(l1,l2)       #第二層開始與原資料公用:[1, 2, 3, [22, 666]] [1, 2, 3, [22, 666]]

print(id(l1))   #2357463048008
print(id(l2))  # 2357463013768
print(id(l1[-1]))  # 2357463047816
print(id(l2[-1]))  # 2357463047816

 

 

 

 


深copy
import copy
l1 = [1,2,3,[22,]]
l2 = copy.deepcopy(l1)
print(l1,l2)   # [1, 2, 3, [22]]    [1, 2, 3, [22]]
l1[-1].append(666)  
print(l1)   # [1, 2, 3, [22, 666]]

print(l2)   # [1, 2, 3, [22]]

 

 

 

 


切片屬於淺copy
l1 = [1,2,3,[22,33]]
l2 = l1[:]
# l1.append(666)
l1[-1].append(666)
print(l2)  # [1, 2, 3, [22, 33, 666]]