item系列
__getitem__(self, item) 對象通過 object[key] 觸發 __setitem__(self, key, value) 對象通過 object[key] = value 觸發 __delitem__(self, key) 對象通過 del object[key] 觸發

class Func: def __getitem__(self, item): # object[item] 觸發 return self.__dict__[item] def __setitem__(self, key, value): # object[key] = value 觸發 self.__dict__[key] = value def __delitem__(self, key): # del object[key] 觸發 print('delitem: 刪除key') del self.__dict__[key] def __delattr__(self, item): # del object.item 觸發 print('delattr: 刪除key') del self.__dict__[item] f = Func() f['name'] = 'hkey' # __setitem__ f['age'] = 20 # __setitem__ print(f.name) # 對象屬性原本的調用方式 print(f['name']) # __getitem__ del f['name'] # __delitem__ print('------') del f.age # __delattr__
要注意反射的 __delattr__ 和 __delitem__ 使用不同的方式觸發不同的特殊方法。

class Fib: def __getitem__(self, item): if isinstance(item, int): a, b = 1, 1 for x in range(item): a, b = b, a+b return a if isinstance(item, slice): start = item.start stop = item.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a+b return L f = Fib() print(f[9]) print(f[:10]) 執行結果: 55 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
__str__ 和 __repr__
當直接打印一個對象的時候,顯示的是一段內存地址。
In [1]: class Person: ...: def __init__(self, name): ...: self.name = name In [2]: p = Person('hkey') In [3]: p Out[3]: <__main__.Person at 0x2be9f5c9128> In [4]: print(p) <__main__.Person object at 0x000002BE9F5C9128>
為了便於更好的理解,我們定義一個__str__方法
In [1]: class Person: ...: def __init__(self, name): ...: self.name = name ...: def __str__(self): ...: return "hello %s." % self.name In [2]: p = Person('hkey') In [3]: p Out[3]: <__main__.Person at 0x2858bd468d0> In [4]: print(p) hello hkey.
定義了__str__方法,直接輸出對象還是打印的內存地址,並沒有走__str__方法中定義的格式,用print輸出信息卻調用了__str__方法定義的內容
In [1]: class Person: ...: def __init__(self, name): ...: self.name = name ...: def __repr__(self): ...: return "hello %s." % self.name In [2]: p = Person('hkey') In [3]: p Out[3]: hello hkey. In [4]: print(p) hello hkey.
定義了__repr__方法,不管是直接打印對象還是通過print打印對象,都是走的__repr__中定義的格式。
總結:
__repr__ 和 __str__ 這兩個方法都是用於顯示的,__str__是面向用戶的,而__repr__是面向程序員
使用print打印操作會首先嘗試__str__和str內置函數,它通常應該返回一個友好的提示
當__str__不存在的時候,會去找__repr__是否定義,定義則打印__repr__中定義的內容
當我們想在所有環境下都統一顯示的話,可以添加__repr__方法。
當我們想在不同的環境下支持不同的顯示,就可以定義__repr__方法和__str__方法,終端交互使用__repr__,用戶使用__str__方法
__new__
__new__ 是在新式類中出現的方法,它作用在構造函數之前,可以這么理解,在python中存在於類里面的構造方法__init__()負責將類實例化,而在__init__() 啟動之前,__new__()決定是否要使用該__init__()方法,因為__new__()可以調用其他類的構造方法或者直接返回別的對象作為本類的實例。
具體參考:https://www.cnblogs.com/ifantastic/p/3175735.html
要記住的是:在實例化時,__new__() 先與 __init__() 方法執行.
通常來講,新式類開始實例化時,__new__()方法會返回cls(cls代指當前類)的實例,然后該類的__init__()方法作為構造方法會接收這個實例(self)作為自己的一個參數,然后依次傳入__new__()方法中接收的位置參數和命名參數。
In [1]: class Foo: ...: def __init__(self, *args, **kwargs): ...: print('in init function.') ...: def __new__(cls, *args, **kwargs): # 實例化時,首先執行__new__方法 ...: print('in new function.') ...: return object.__new__(cls, *args, **kwargs) In [2]: f = Foo() in new function. in init function.
對於__new__ 和 __init__ 的個人理解如下:
首先,我們把一個類比一個生物,使用__new__方法是創建這個生物的本體(實例),當本體(實例)創建好,才能使用__init__來創建這個本體的屬性
一個單例模式來印證上面的描述:
In [1]: class Foo: ...: def __init__(self, *args, **kwargs): ...: pass ...: def __new__(cls, *args, **kwargs): ...: if not hasattr(cls, '_instance'): ...: cls._instance = object.__new__(cls, *args, **kwargs) ...: return cls._instance In [2]: one = Foo() In [3]: two = Foo() In [4]: id(one) Out[4]: 2173030026152 In [5]: id(two) Out[5]: 2173030026152 In [6]: print(one == two) True In [7]: print(one is two) True
通過__new__方法,首先創建實例的本體,當第二次實例化時,通過if判斷,這個本體已經存在,就直接返回,然后在調用__init__()方法附加屬性值
python3 正常的使用格式:
class Foo(object): def __new__(cls, *args, **kwargs): return object.__new__(cls) def __init__(self, name): self.name = name f = Foo('hkey') print(f.name)
__del__
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:此方法一般無需定義,因為python是一門高級語言,程序員在使用時無需關心內存和釋放,因為此工作都是交給python解釋器來執行的,所有,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
在 pycharm中當程序執行完,自動觸發 __del__ 方法
class Foo: def __init__(self, name): self.name = name def __del__(self): print('執行__del__.') f = Foo('hkey') # 執行結果: 執行__del__. __del__ 在類中適合做一些關閉文件句柄等操作。
__call__
當對象+()時候[object()] 觸發
class Foo: def __init__(self, name): self.name = name def __call__(self, *args, **kwargs): print('執行__call__.') f = Foo('hkey') f() # 執行結果: 執行__call__.
__len__
使用 len(object) 觸發
class Foo: def __init__(self, name): self.name = name def __len__(self): return 10 # 注意返回結果必須是 int 類型 f = Foo('hkey') print(len(f)) # 執行結果: 10
__hash__
調用 hash(object) 觸發
class Foo: def __init__(self): self.a = 1 self.b = 5 def __hash__(self): return hash(str(self.a) + str(self.b)) f = Foo() print(hash(f)) # 執行結果: 2068706206124340336
__eq__
當兩個對象進行比較時,觸發
class Foo: def __init__(self): self.a = 1 self.b = 5 def __eq__(self, other): if self.a == other.a and self.b == other.b: return True return False a = Foo() b = Foo() print(a == b) # 執行這個比較的時候,就調用了 __eq__ 方法 # 執行結果: True
實例練習:紙牌

from collections import namedtuple Card = namedtuple('Card', ['rank', 'suit']) class FranchDeck: ranks = [x for x in range(2, 11)] + list('JQKA') suits = ['紅桃', '黑桃', '方塊', '梅花'] def __init__(self): '''創建一副牌''' self._card = [Card(rank, suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): '''統計牌數''' return len(self._card) def __getitem__(self, item): '''通過object[index]取牌''' return self._card[item] def __setitem__(self, key, value): '''調用 shuffle 的時候需要有__setitem__方法''' self._card[key] = value f = FranchDeck() print(f[:4]) # 按照順序取牌 from random import shuffle shuffle(f) # 隨機排列 print(f[:4]) # 切片隨機取4張 # 執行結果: [Card(rank=2, suit='紅桃'), Card(rank=2, suit='黑桃'), Card(rank=2, suit='方塊'), Card(rank=2, suit='梅花')] [Card(rank=7, suit='紅桃'), Card(rank=3, suit='方塊'), Card(rank='J', suit='紅桃'), Card(rank=5, suit='黑桃')]