前言: 本文一切觀點和測試代碼是在python3的基礎上。 Content: 1.什麼是魔法函數,魔法函數__getitem__在python中應用。 2.python的數據模型和數據模型這種設計對python的影響 3.python常用的魔法函數 4.從len()方法看魔法函數的特點 5.魔法函數 ...
前言:
本文一切觀點和測試代碼是在python3的基礎上。
Content:
1.什麼是魔法函數,魔法函數__getitem__在python中應用。
2.python的數據模型和數據模型這種設計對python的影響
3.python常用的魔法函數
4.從len()方法看魔法函數的特點
5.魔法函數知識小結
一 python的魔法函數
1.什麼是魔法函數?
- 魔法函數是Python中定義的,以__開頭,__結尾,形如__fun__()的函數,一般使用已經定義好了的即可。
- 使用這樣一些函數,可以讓我們自定義的類有更加強大的特性。
- 魔法函數一般是隱式調用的,不需要我們顯示調用。(即python解釋器幫我們調用實現)
2.如何使用魔法函數?
例:我們建立一個公司類,裡面有員工列表屬性。需要迴圈列印所有員工。
普通方法為:
1 class Company: 2 def __init__(self,employee_list): 3 self.employee=employee_list 4 user_list=['ttr1','ttrr2','ttrr3'] 5 company=Company(user_list) 6 for i in company.employee: 7 print(i)
如果說我們用python一個內置的魔法函數的話:
user_list=['ttr1','ttrr2','ttrr3'] company=Company(user_list) class Company_Magic: def __init__(self,employee_list): self.employee_list=employee_list def __getitem__(self, item): return self.employee_list[item] company2=Company_Magic(user_list) for i in company2: print(i)
區別:第二種直接對對象進行了for迴圈,第一種對對象裡面的一個屬性(list)進行迴圈。
就是第二種Company_Magic類生成的對象,是有可以迭代這個特性的。
因為__getitem__幫我們實現了一個邏輯,每次我們實現for迴圈的時候,for會去找company2這個對象中的__getitem__方法(實際上是先找另外一個迭代方法),並且傳入0、1、2...直到拋了異常然後結束。這是解釋器幫我們實現的功能。
ps.想想看如果__getitem__這個函數不管傳入什麼item都不報異常會怎麼樣?
二 python的數據模型和數據模型這種設計模式賦予python的特性
1.什麼是數據模型?
數據模型其實是對 Python 框架的描述,它規範了這門語言自身構建模塊的介面,這些模塊包括但不限於序列、迭代器、函數、類等。
而魔法函數就是數據模型的一個概念。java中常被叫魔術方法,python常叫數據模型。
2.數據模型對python的影響
- 魔法函數不屬於定義它的那個類,只是增強了類的一些功能。
- 實現了特定的魔法函數之後,某些操作會變得特別簡單。
- 我們可以採用實現魔法函數來靈活地設計我們需要的類。
這點是很重要的需要明白python的類有些特性是可以被靈活設計的,只要在編碼中讓它遵循了某種特定的協議(魔法函數)。
三 python常用的魔法函數
__init__:最常用。
__str__:被print函數調用;這個魔法函數必須返回為string,否則拋異常。
(我記得最開始初學python調用mongodb對象的什麼方法來著,返回的數據用print出來的字元串一直沒問題,但是做處理的時候一直說數據格式有誤,就是因為傳回來的數據對象做了__str__處理。)
__len__:可以對對象使用 len()函數。在dict、list等類型中也實現了這個方法。
在pycharm中,輸入dict() ,用ctrl+B 跳到dict的定義,可以看到dict具有哪些魔法方法。或者用dir({})也可以看到。
還有很多比較重要的魔法方法例如 __setattr__() 、__getattr__()、__setitem__()、__getitem__()、__iter__() 超多簡化我們編程的魔法函數。
四 用__len__看魔法函數大致做了哪些事情?
在三中我們看到,dict()的定義中的魔法函數__len__()定義的內容為空,list()也是。
實際上,我們在這裡看到的相當於只是一個介面的定義而已,真正的實現是在cpython中。
為了加快len()的速度,比如list、dict、set類型,實際上會走一個捷徑。在cpython中,list、dict、set等內置類型是用c實現的,會在c數據結構內部有個參數用來存儲數據的長度,len()是直接去讀數據結構的長度。所以複雜度是O(1)。 以下為拓展,可不看:有關 list 定義源碼位置在 Python 目錄下的 include/listobject.h
內, 代碼如下:
typedef struct { PyObject_VAR_HEAD /* Vector of pointers to list elements. list[0] is ob_item[0], etc. */ PyObject **ob_item; /* ob_item contains space for 'allocated' elements. The number * currently in use is ob_size. * Invariants: * 0 <= ob_size <= allocated * len(list) == ob_size * ob_item == NULL implies ob_size == allocated == 0 * list.sort() temporarily sets allocated to -1 to detect mutations. * * Items must normally not be NULL, except during construction when * the list is not yet visible outside the function that builds it. */ Py_ssize_t allocated; } PyListObject;
其中 ob_item
是指向列表元素的指針數組, list[0] 即 ob_item[0], allocated
是列表的空間大小。在 PyObject_VAR_HEAD
中, 擁有一個 ob_size
變數。
下麵是 Python 目錄下的 include/object.h
中的相關代碼:
#define PyObject_VAR_HEAD PyVarObject ob_base; ... ... typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;
ob_size
變數存儲的就是對象的長度, 所以每次調用 _len_() 方法的時候, 返回的是一個已經存儲好了的變數,
五 總結
魔法函數是貫穿所有python知識點的。 可以用魔法函數來組織自己腦子裡python知識點的知識樹。- 魔法函數是內置的前面為__的方法
- 魔法函數是不需要顯示調用的,python語法本身會隱含調用魔法函數
- 魔法函數和對象不存在繼承關係,任何對象都可以定義魔法函數
- 魔法函數讓python的各種類型組織起來了,讓對象有數據類型,比如增加一個迭代類型等
- __str__\__repr__\__add__等等多種常用魔法函數熟知會讓代碼更pythonic
- python解釋器其實會經可能提高很多效率,讓開發者靈活性變高