Python高階知識點學習(五)

yi杯咖啡發表於2018-10-24

dict的子類

首先,不建議自己編寫程式碼繼承dict和list這種原生使用c語言編寫的,因為有時候,用c語言寫的dict不會呼叫python寫的覆蓋的方法。

如果確實有繼承dict來寫程式碼的需求,可以使用UserDict,繼承這個UserDict。
UserDict這個內部使用了python語言實現了c語言寫的邏輯。

from collections import UserDict

class Mydict(UserDict):
    def __setitem__(self, key, value):
        super().__setitem__(key, value*2)

my_dict = Mydict(one=1)
print (my_dict)

列印結果:
{`one`: 2}

set和fronzenset

set集合:

  1. 無序
  2. 不重複
  3. set 接受一個可迭代物件

frozenset集合:

  1. 一旦設定好就無法修改。
  2. frozenset為不可變型別。
  3. 相對於可變型別來說的好處,可以作為dict的key。

set的初始化方法:

set([a, b, c])
a = {a, b, c}
兩種都可以初始化

dict、set實現原理

當我們瞭解了背後的實現原理,就可以判斷什麼情況下使用dict以及為什麼要使用dict。
dict查詢的效能遠遠大於list。

在list中隨著list資料的增大 查詢時間會增大。
在dict中查詢元素不會隨著dict的增大而增大。

dict原理實際上就是利用hash演算法。

陣列和連結串列相比來說最大的優勢,就是它可以做到任何一個位置直接存取而不需要從頭到尾遍歷,因為陣列是一段連續的空間,陣列取資料的時間複雜度是O(1)。

dict的key或者set的值,都必須是可以hash的。不可變物件都是可hash的, str, fronzenset, tuple。

自己實現的類過載__hash__這個魔法函式,讓它可以變為可雜湊物件。

dict的記憶體花銷大,但是查詢速度快, 自定義的物件或者python內部的物件都是用dict包裝的。

dict的儲存順序和元素新增順序有關,新增資料有可能改變已有資料的順序。

Python中的變數是什麼

python和java中的變數本質不一樣,python的變數實質上是一個指標。我們可以理解變數就是一個便利貼,

例如:a = 1
先成物件,然後貼便利貼,把a貼在1上面。

a = [1, 2, 3]
b = a
print(id(a), id(b))
列印結果:
a和b的地址值一樣。

is 和 == 的區別

is是判斷物件的id是否相同,但是注意看下邊例子:

a = [1, 2, 3, 4]
b = [1, 2, 3, 4]
print (id(a), id(b))
print (a is b)
輸出結果:
4446597320 4446597640
False

上邊這種用法得到的結果很正常,再看下邊程式碼:

a = 1
b = 1
print (id(a), id(b))
print (a is b)

執行結果:
4325627840 4325627840
True

這種情況下,a和b指向的是同一個。這是由於Python內部機制決定的,將小正數建立一個全域性唯一的物件,小段字串也是一樣的。

a = [1, 2, 3]
b = [1, 2, 3]
print(a==b) 返回True

因為:
list裡邊實現了一個魔法函式 __eq__,當我們呼叫a==b這種模式的時候,會呼叫__eq__這個魔法函式,從而來判斷值是否相等。
它們的值是相等的,只是不是同一個物件而已,所有a==b是True。

垃圾回收和del語句

cpython中垃圾回收的演算法是採用 引用計數

a = 1
b = a
此時,1這個物件上就又有一個計數器,a = 1 時會在計數器上加1,b如果指向的還是1,1上邊的計數器會再加1,當不使用時,執行del a,他就會將引用計數器減1,當引用計數器減到0時,python直譯器會將物件回收。

物件只有在計數器減到0時,才會被回收,del只是減計數器的功能。

a = object()
b = a
del a
print(b)
print(a)

列印結果:
<object object at 0x104a86100>
NameError: name `a` is not defined

可以看到,執行del後,只是把a銷燬了,b還在。

__del__魔法函式:
可以在__del__魔法函式中實現自己的邏輯,當python直譯器回收物件的時候,會呼叫物件的__del__魔法函式,它可以幫我們在回收物件時做一些事。

@property的用法

我們在讀原始碼時,往往會看到這這種方法:

@property
def hello(self):
     pass

@property 這個裝飾器會把函式變為屬性描述符,怎麼說?

看程式碼:

class Allen(object):

    def word(self):
        return `word`

    @property
    def hello(self):
        return `hello`

a = Allen()
print(a.hello)
print(a.word())

執行結果:
hello
word

可以發現,@property這個裝飾器把取方法的模式變為取屬性。

魔法函式__getattr____getattribute__介紹

魔法函式是python直譯器內部需要用的方法,它是整個python動態特性的最根本原因。

__getattr__:就是在查詢不到屬性的時候呼叫。
例1:

class User:
    def __init__(self, info):
        self.info = info

    def __getattr__(self, item):
        return `2`

if __name__ == "__main__":
    user = User(`allen`)
    print(user.info)
    print(user.age)

執行結果:
allen
2

例2:

class User:
    def __init__(self, info={}):
        self.info = info

    def __getattr__(self, item):
        return self.info[item]

if __name__ == "__main__":
    user = User(info={"name": "allen", "age": "3"})
    print(user.name)

列印結果:
allen

__getattribute__ : 比__getattr__更高階,只要查詢屬性,就會首先進入__getattribute__這個魔法函式,強制進入,無條件的。

__getattribute__這個魔法函式儘量不要去重寫,因為如果一旦寫不好,整個類的屬性訪問就會崩潰掉,一般寫框架時會用到這個魔法函式。

class User:
    def __init__(self,info={}):
        self.info = info

    def __getattr__(self, item):
        return self.info[item]

    def __getattribute__(self, item):
        return "10"

if __name__ == "__main__":
    user = User(info={"name":"allen"})
    print(user.name)
    print(user.test)

列印結果:
10
10

屬性描述符和屬性查詢過程

一個類只需要實現__get____set____delete__這三個中的任意一個方法,它就算是屬性描述符。

通過屬性描述符,可以控制在賦值的時候它的行為,在屬性設定的時候引數檢查。

屬性描述符有兩種:

  1. 資料屬性描述符:實現__get____set__就是資料屬性描述符。
  2. 非資料屬性描述符:只實現一個__get__方法就是非資料屬性描述符。

資料屬性描述符 和 非資料屬性描述符 它們的屬性查詢過程是不一樣的。

前邊提到的屬性查詢過程,先查詢例項中的屬性,然後查詢類中的屬性,實際上它有更加詳細的查詢過程。

魔法函式__new____init__ 區別

__new__ 魔法函式在python新式類才會有 python2.2之前沒有這個。

class User:
    def __new__(cls, *args, **kwargs):
        print(" in new ")
        return super().__new__(cls)

    def __init__(self):
        print(" in init")
        pass

if __name__ == "__main__":
    user = User()

列印結果:
 in new 
 in init

__new__魔法函式允許在生成物件之前加邏輯,自定義物件生成過程,傳遞進來的是類。

__init__方法傳遞進去的是物件。

__new____init__ 之前呼叫。

__new__中必須return super().__new__(cls)才會呼叫__init__方法

def __new__(cls, *args, **kwargs):這個中的*args,和**kwargs,代表的是傳入的引數。


相關文章