Python學習手冊之內部方法、操作符過載和物件生命週期
在上一篇文章中,我們介紹了 Python 的類和繼承,現在我們介紹 Python 的內部方法、操作符過載和物件生命週期。
檢視上一篇文章請點選:https://www.cnblogs.com/dustman/p/10016359.html
內部方法和操作符過載
內部的方法
Python 裡有一些特殊的方法,也就是以雙下劃線開頭並且以雙下劃線結尾的。它們可以是變數如__doc__ ,也可以是方法如__init__ 。
它們常見用途是操作符過載。這意味著可以自定義類的操作符,允許在這些類上使用加減乘除等運算子 。
下面程式碼實現了__call__ 方法的物件,相當於過載了 (),可以實現呼叫功能。 實現非波納契數列 的類
。
class Fib(): def __call__(self, *args, **kwargs): ret = [1,1] num = int(args[0]) if num == 1: return [1,] else: while len(ret)< num: ret.append(ret[-1]+ret[-2]) return ret fib = Fib() print(fib(7))
執行結果:
>>> [1, 1, 2, 3, 5, 8, 13] >>>
斐波那契數列,是數學上一個無窮數列。其形式為 1,1,2,3,5,8,13…… 從第三項開始,每一項都是前兩項之和。
類運算過載的方法:
__sub__ 表示運算子-
__mul__ 表示運算子*
__truediv__ 表示運算子/
__floordiv__ 表示運算子//
__mod__ 表示運算子%
__pow__ 表示運算子**
__and__ 表示運算子&
__xor__ 表示運算子^
__or__ 表示運算子|
表示式x + y 會被 Python 轉換成x.__add__(y) 。但是,如果x 沒有實現__add__ 方法並且x 和y 是不同的型別,那麼會呼叫y.__radd__(x) 。
對於上面提到的所有方法,都有相等的新增 r 字首的方法。
class Foo: def __init__(self,text): self.text = text def __mul__(self, other): line = "=" * len(other.text) return "\n".join([self.text,line,other.text]) msg = Foo("Hello") hello = Foo("I like Python!") print(msg * hello)
執行結果:
>>> Hello ============== I like Python! >>>
上面例子中,我們定義了類 Foo 的一個乘法方法。
Python 同樣提供了資料比較方面的方法。
__it__ 表示運算子<
__le__ 表示運算子<=
__eq__ 表示運算子==
__ne__ 表示運算子!=
__gt__ 表示運算子>
__ge__ 表示運算子>=
如果類沒有實現__ne__ ,則返回__eq__ 相反的值。其他運算子之間沒有這樣的關係。
class FooString(): def __init__(self, name): self.name = name def __lt__(self, obj): return self.name == obj.name a = FooString("Python") b = FooString("Html") print(a < b)
執行結果:
>>> False >>>
類中還有一些方法:
__len__ 可用來做len()
__getitem__ 可用來做鍵值讀取,適用於[] 運算子。
__setitem__ 設定給定值的值。
__delitem__ 刪除給定鍵對應的元素
__iter__ 用來遍歷物件
__contains__ 用來做in 操作
還有許多其他的神奇方法,我們在這裡就不一一談論了,例如__call__ 用於將物件作為函式呼叫,__init__,__str__ 等等用於將物件轉換為 Python 基礎資料型別。
#把一個類做成一個字典 class Foo(): def __init__(self): self.data = {} def __getitem__(self, key): print('__getitem__', key) return self.data.get(key) def __setitem__(self, key, value): print('__setitem',key, value) self.data[key] = value def __delitem__(self, key): #觸發del print('__delitme__', key) obj = Foo() #例項化Foo obj['name'] = 'Python' #執行__setitem__ print(obj['name']) #執行__getitem__ del obj["name"]
執行結果:
>>> __setitem name Python __getitem__ name Python __delitme__ name >>>
鍵值讀取函式 __getitem__ 還可以根據表示式返回字典中的 key,__setitem__ 設定 key 對應的 value 值。
物件生命週期
一個物件會經歷三個生命週期:建立,操作,銷燬。
物件生命週期的第一階段是它類變數和方法的定義。
接下來下一階段是這個例項的例項化。當__init__ 被呼叫是,記憶體分配給儲存例項。在此之前,呼叫類的__new__ 方法,這通常僅在特殊情況下會被重寫。在此以後,該物件就可以使用了。
現在,其他程式碼可以通過呼叫物件上的函式或訪問其屬性與物件進行互動。最後它使用完畢並可被銷燬。
當一個物件被銷燬時,分配給它的記憶體會被釋放,並可以用於其他目的。
當物件的引用計數達到零時,物件將銷燬。引用計數是指引用物件的變數和其他元素的數量。如果沒有任何變數引用它(它的引用計數為零),意味著沒有任何東西可以與其互動,因此可以安全地刪除它。在此也可以被刪除。del 語句將物件的引用計數減少一個,這通常會導致物件被刪除。del 語句呼叫物件的方法__del__ 。
在不再需要物件是刪除物件的過程稱為垃圾收集。物件的引用計數在分配新名稱或放在容器 (列表、元組或字典) 中時會增加。當使用 del 刪除物件時,物件的引用計數會減少。當物件的引用計數達到零時,Python 會自動刪除它。
a=1# 物件 1 被 變數a引用,物件1的引用計數器為1 b=a# 物件1 被變數b引用,物件1的引用計數器加1 c=a# 物件1 被變數c引用,物件1的引用計數器加1 del a#刪除變數a,解除a對1的引用 del b#刪除變數b,解除b對1的引用 del c#刪除變數C,解除C對1的引用
上面的例子,建立了一個含 1 的物件,物件的計數器加了 3 次,然後物件的計數器減了三次。這時該物件會被自動記憶體管理銷燬。 像 C 這樣的低階語言沒有這種自動記憶體管理系統。
“數學就是用來把七成人篩出去的。”