1. 程式人生 > >記錄我的 python 學習歷程-Day06 is id == / 程式碼塊 / 集合 / 深淺拷貝

記錄我的 python 學習歷程-Day06 is id == / 程式碼塊 / 集合 / 深淺拷貝

一、is == id 用法

  在Python中,id是記憶體地址, 你只要建立一個數據(物件)那麼就會在記憶體中開闢一個空間,將這個資料臨時載入到記憶體中,這個空間有一個唯一標識,就好比是身份證號,標識這個空間的叫做記憶體地址,也就是這個資料(物件)的id,那麼你可以利用id()去獲取這個資料的記憶體地址:

name = 'Dylan'
print(id(name)) # 4319709032

​ == 是比較兩邊的數值是否相等,其反回的結果是 True 或 False。

​ is 是比較兩邊的記憶體地址是否相等,如果記憶體地址相等,那麼兩邊所指向是同一個記憶體地址。其反回的結果是 True 或 False。

name = ['Dylan']
name2 = ['Dylan']
print(name is name2)    # False
print(name == name2)    # True
print(id(name))         # 4387384328
print(id(name2))        # 4387382920

# 這裡表示:name name2數值是一樣的,但卻不是同一個記憶體地址。

​ 所以:如果記憶體地址相同,那麼值肯定相同,但是如果值相同,記憶體地址不一定相同。

二、程式碼塊

  • Python 程式是由程式碼塊構造的,塊是一個 python 程式的文字,他是作為一個單元執行的。

    程式碼塊:一個模組、一個函式、一個類、一個檔案等都是一個程式碼塊。

  • 作為互動方式輸入的每個命令都是一個程式碼塊。

    什麼是互動方式?

    就是咱們在 cmd 中進入 Python 直譯器裡面,每一行程式碼都是一個程式碼塊。

三、同一程式碼塊下的快取機制

  • 前提條件:同一個程式碼塊內。
  • 機制內容:Python在執行同一個程式碼塊的初始化物件的命令時,會檢查是否其值是否已經存在,如果存在,會將其重用。換句話說:執行同一個程式碼塊時,遇到初始化物件的命令時,他會將初始化的這個變數與值儲存在一個字典中,在遇到新的變數時,會先在字典中查詢記錄,如果有同樣的記錄那麼它會重複使用這個字典中的之前的這個值。所以在你給出的例子中,檔案執行時(同一個程式碼塊)會把i1、i2兩個變數指向同一個物件,滿足快取機制則他們在記憶體中只存在一個,即:id相同。
  • 適用物件:int(float)、bool、str。
  • 具體細則:
    • int(float):任何數字在同一程式碼塊下都會複用。
    • bool:True 和 False 在字典中會以1,0的方式存在,並且複用。
    • str:幾乎所有的字串都會符合快取機制。
  • 優點:提升效能,節省記憶體。

不同程式碼塊下的快取機制(小資料池)

  • 前提條件:不同程式碼塊內。

  • 機制內容:Python自動將-5~256的整數進行了快取,當你將這些整數賦值給變數時,並不會重新建立物件,而是使用已經建立好的快取物件。

    python會將一定規則的字串在字串駐留池中,建立一份,當你將這些字串賦值給變數時,並不會重新建立物件, 而是使用在字串駐留池中建立好的物件。

      其實,無論是快取還是字串駐留池,都是python做的一個優化,就是將~5-256的整數,和一定規則的字串,放在一個‘池’(容器,或者字典)中,無論程式中那些變數指向這些範圍內的整數或者字串,那麼他直接在這個‘池’中引用,言外之意,就是記憶體中之建立一個。

  • 適用物件:int(float)、bool、str。

  • 具體細則:

    • int(float):那麼大家都知道對於整數來說,小資料池的範圍是-5~256 ,如果多個變數都是指向同一個(在這個範圍內的)數字,他們在記憶體中指向的都是一個記憶體地址。
    • bool:True 和 False 在字典中會以1,0的方式存在,並且複用。
    • str:滿足規則的字串。
  • 優點:提升效能,節省記憶體。

  • 總結:
    • 面試題考。
    • 回答的時候一定要分清楚:同一個程式碼塊下適用一個快取機制。不同的程式碼塊下適用另一個快取機制(小資料池)
    • 小資料池:數字的範圍是-5~256.
    • 快取機制的優點:提升效能,節省記憶體。

四、集合(瞭解)

​ 集合是無序的,不重複的資料集合,它裡面的元素是可雜湊的(不可變型別),但是集合本身是不可雜湊(所以集合做不了字典的鍵)的。

  • 集合最重要的兩點:
    • 去重,把一個列表變成集合,就自動去重了。
    • 關係測試,測試兩組資料之前的交集、差集、並集等關係。
  • 集合和字典

    • 集合的建立

      # 方法一
      set1 = set({'name', 'Dyaln', 'age', 111, 434})
      # 方法二
      set1 = {'name', 'Dyaln', 'age', 111, 434}
    • 字典和集合的格式:

      # 字典
      dic = {'name':'Dylan', 'age': 18}
      # 集合
      set1 = {'name', 'age', 18, False, True, }
    • 空字典:

      dic = {}
      # 或者
      {}
      print({}, type({}))     # {} <class 'dict'>
    • 空集合:

      set()
      print(set(), type(set()))       # set() <class 'set'>
    • 集合的有效性:

      set1 = {[1, 3, 5], 3, {'name': 'Dylan'}}
      print(set1)     
      # 報錯
        File "/Users/yaoyaoba/Full_stack_22/day06/練習.py", line 24, in <module>
      set() <class 'set'>
          set1 = {[1, 3, 5], 3, {'name': 'Dylan'}}
      TypeError: unhashable type: 'list'
      # 集合內的元素必須是 可合希型別(不可變資料型別)
  • 集合的操作

      • set.add()

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        set1.add('xx')
        print(set1)     # {'xiaowang', 'xx', 'age', 'yaoyao', 'name', 'Dylan'}
      • set.update() 迭代增加(有重複的會自動除去)

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        set1.update('abcdedfdaefdafdsa')
        print(set1)     # {'yaoyao', 'age', 'd', 'e', 'a', 'Dylan', 'xiaowang', 'b', 'f', 'c', 'name', 's'}
      • set.remove() 按元素刪除

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        set1.remove('age')
        print(set1)     # {'xiaowang', 'yaoyao', 'Dylan', 'name'}
      • set.pop() 隨機刪除

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        set1.pop()
        print(set1)      # {'yaoyao', 'age', 'xiaowang', 'Dylan'}
      • set.clear() 清空集合

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        set1.clear()
        print(set1)     # set()
      • del set 刪除集合

        set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'}
        del set1
        print(set1)     # 會報錯,因為己經沒有這個集合了
        # 報錯資訊如下:
        Traceback (most recent call last):
          File "/Users/yaoyaoba/Full_stack_22/day06/練習.py", line 28, in <module>
            print(set1)     # 會報錯,因為己經沒有這個集合了
        NameError: name 'set1' is not defined
    • 集合的其它操作

      • 交集。(& 或者 intersection)

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

        set1 = {1, 2, 3, 4, 5}
        set2 = {4, 5, 6, 7, 8}
        print(set1 | set2)  # {1, 2, 3, 4, 5, 6, 7, 8} 
      • 差集。(- 或者 difference)

        set1 = {1, 2, 3, 4, 5}
        set2 = {4, 5, 6, 7, 8}
        print(set1 - set2)  # {1, 2, 3} 
      • 反交集。 (^ 或者 symmetric_difference)

        set1 = {1, 2, 3, 4, 5}
        set2 = {4, 5, 6, 7, 8}
        print(set1 ^ set2)  # {1, 2, 3, 6, 7, 8}
      • 子集與超集

        set1 = {1,2,3}
        set2 = {1,2,3,4,5,6}
        
        print(set1 < set2)
        print(set1.issubset(set2))  # 這兩個相同,都是說明set1是set2子集。
        
        print(set2 > set1)
        print(set2.issuperset(set1))  # 這兩個相同,都是說明set2是set1超集。
      • 列表去重

         l1 = [1,'Dylan', 1, 2, 2, 'Dylan',2, 6, 6, 3, 'Dylan', 4, 5]
        # set1 = set(l1)
        # l1 = list(set1)
        # print(l1)
        
        # 用處:資料之間的關係,列表去重。

五、深淺copy

​ copy其實就是複製一份,也就是所謂的抄一份。深淺copy其實就是完全複製一份,和部分複製一份的意思。

  • 先看賦值運算

    l1 = [1, 2, 3, ['Dylan', 'age']]
    l2 = l1
    l1.append(456)
    print(l1)   # [1, 2, 3, ['Dylan', 'age'], 456]
    print(l2)   # [1, 2, 3, ['Dylan', 'age'], 456]
    print(id(l1))   # 4387382920 記憶體地址是一樣的
    print(id(l2))   # 4387382920 記憶體地址是一樣的

    對於賦值運算來說,l1與l2指向的是同一個記憶體地址,所以它們是完全一樣的,l1,l2指向的是同一個列表,任何一個變數對列表進行改變,剩下那個變數在使用列表之後,這個列表就是發生改變之後列表。

  • 淺拷貝 copy

    l1 = [1, 2, 3, ['Dylan', 'age']]
    l2 = l1.copy()
    print(id(l1))   # 4335892104
    print(id(l2))   # 4335903304
    # 這說明,通過 copy 出來的新列表,在記憶體中又開闢了一塊新的記憶體空間,兩者間不是指向的同一個列表。
    # 但是,如果再做如下操作你會發現什麼?
    
    print(id(l1[-1]))   # 4370607112
    print(id(l2[-1]))   # 4370607112
    # 你會發現,咦?記憶體地址是一樣的,說明是同一個資料。

    由此我們可以得知,淺拷貝其實只是拷貝了一個列表的外殼。

    對於淺copy來說,只是在記憶體中重新建立了開闢了一個空間存放一個新列表,但是新列表中的元素與原列表中的元素是公用的。

    這裡還有一個問題:

    當改變列表中的不可變資料型別時,新列表中的內容是不會一同被更改的,因為它是可雜湊資料型別,如列表中,可變資料型別被列改或增刪改,則新列表會一同更改。

  • 深拷貝 deepcopy

    import copy
    l1 = [1, 2, 3, ['Dylan', 'age']]
    l2 = copy.deepcopy(l1)
    print(id(l1))   # 4343088456
    print(id(l2))   # 4370618248
    # 這說明,通過 copy 出來的新列表,在記憶體中又開闢了一塊新的記憶體空間,兩者間不是指向的同一個列表。
    
    print(id(l1[-1]))   # 4379005512
    print(id(l2[-1]))   # 4379005832
    # 咦?記憶體地址不一樣了,說明不是同一個資料了。
    
    print(id(l1[0]))    # 4305226112
    print(id(l2[0]))    # 4305226112
    # 哎我去!又一樣了,咋回事兒?

    深 copy 的特性就是將可變的資料型別在記憶體中重新建立一份,而不可變的資料型別則沿用之前的。

    但,同樣如淺拷貝那樣,不可變的資料型別,即便記憶體地址相同,當你改變他時,新列表也不會一同被更改,只因為他是不可變資料型別(可雜湊)。

  • 相關面試題

    l1 = [1, 2, 3, [22, 33]]
    l2 = l1[:]
    l1[-1].append(666)
    print(l1)   # [1, 2, 3, [22, 33, 666]]
    print(l2)   # [1, 2, 3, [22, 33, 666]]
    淺copy: list dict: 巢狀的可變的資料型別是同一個。
    深copy: list dict: 巢狀的可變的資料型別不是同一個 。