閱讀別人撰寫的Python代碼時,經常會在他們定義的類中看到以“__”為開頭和結尾的方法,經過進一步學習后才知道類中實作的這類方法被稱為“魔術方法”,“魔術方法”在一些情況下會被自動的呼叫,通過一些簡單的定義就可以實作比較神奇的功能,如果你希望根據自己的需求去實作具有“特殊”功能的類,那么就需要對這些方法進行重寫,下面內容將對一些常用的“魔術方法”進行介紹,
1、__init__方法
__init__方法功能有點類似于其它面向物件語言中的建構式,在類實體化物件時它會被自動的呼叫,一般情況下,在定義的類中都會實作__init__方法,在__init__方法內根據創建類物件時傳入引數對類物件的屬性進行初始化操作,
class student(object): def __init__(self, _name, _age): self.name = _name self.age = _age def get_info(self): print('%s is %d years old' %(self.name, self.age))
測驗結果:
>>> stu = student("Michael", 12) >>> stu.get_info() Michael is 12 years old
2、__new__方法
前面的__init__方法承擔了類實體化物件后對類物件屬性必要的初始化操作,而__new__方法則是在__init__方法之前被呼叫創建類物件,與__init__方法不同的是__new__方法必須回傳一個值,回傳所創建物件的實體,
__new__方法定義形式:
def __new__(cls, *args, **kw): pass
__new__方法的首個引數是cls,因此,它是屬于一個類方法,這也是我們可以通過object.__new__來呼叫它的原因,
不過,一般情況下,在定義的類中很少去重寫__new__方法,但在實作單例設計模式時,可通過重寫__new__方法實作,
class single(object): _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(single, cls).__new__(cls, *args, **kwargs) return cls._instance def __init(self): pass
上述single類,通過重寫了__new__方法,實作了單例設計模式,_instance是single類的屬性,用于描述是否已經創建了實體物件,在創建一個single物件時,首先根據類屬性_instance判斷是否是第一次創建物件,如果是呼叫父類的__new__方法創建物件,如果不是,就回傳之前創建的物件,
測驗結果:
>>> obj1 = single() >>> obj2 = single() >>> obj1 <__main__.single object at 0x0000000003D44F08> >>> obj2 <__main__.single object at 0x0000000003D44F08>
3、__call__方法
該方法的功能類似于在類中多載()運算子,使得類實體物件可以像呼叫普通函式那樣,以“類物件名()”形式使用,呼叫的結果即呼叫到__call__方法,
class call_cls(object): def __call__(self): print('__call__ function...')
測驗結果:
>>> obj = call_cls() >>> obj() __call__ function...
4、__str__方法
如果類中實作了__str__方法,當使用print輸出物件時,那么就會列印出從這個方法中回傳的資料
class cat(object): def __init__(self, _name, _hobby): self.name = _name self.hobby = _hobby def __str__(self): return ('%s hobby is %s' %(self.name, self.hobby))
測驗結果:
>>> Tom = Cat('Tom', 'eat fish') >>> print(Tom) Tom hobby is eat fish
5、__iter__和__next__方法
如果一個類想像list、tuple那樣,可以用于for...in回圈,那么它就要在內部實作一個__iter__方法,該方法回傳一個迭代物件,然后Python的for回圈不斷的呼叫該迭代物件的__next__方法來獲取回圈的下一個值,直到遇到StopIteration例外時退出回圈,
現在我們來寫一個可用于for...in回圈的類,這個類用來計算斐波那契數列
先來看第一種寫法:
class Fib_Calc(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __next__(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100: # 退出回圈的條件 raise StopIteration() return self.a # 回傳下一個值 class Fib(object): def __iter__(self): return Fib_Calc()
Fib類中實作__iter__方法,該方法中要回傳一個可迭代物件,該物件是Fib_Calc類的實體化,所以,Fib_Calc中還應包含__next__方法
Fib_Calc類中的__next__方法根據斐波那契數列特點,計算一次結果
測驗結果:
>>> fib = Fib() >>> for i in fib: print(i) 1 1 2 3 5 8 13 21 34 55 89
再來看第二種寫法:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __iter__(self): return self # 實體本身就是迭代物件,故回傳自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100000: # 退出回圈的條件 raise StopIteration() return self.a # 回傳下一個值
我們可以不像第一種寫法那樣,直接將__iter__和__next__方法在同一個類中實作,一個類包含__next__方法,這個類就是一個可迭代類,在__iter__方法中只需要回傳這個物件自身,那么回傳的結果就是一個可迭代物件,
這里同時實作__iter__和__next__方法的物件,也就是我們所說的“迭代器”,
6、__getitem__方法
上面使用__iter__和__next__方法,實作了類能像list和tuple一樣使用for...in回圈,那么類是否能像list那樣按下標取出元素呢?
當然是可以的,如果類要表現得像list那樣按照下標取出元素,需要在類中實作__getitem__方法,仍然以斐波拉契數列為例進行演示
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a
現在,就可以按下標訪問數列的任意一項了:
>>> fib = Fib() >>> fib[2] 2 >>> fib[4] 5
已經實作了下標取資料,但是要像list一樣使用切片功能,還需要在__getitem__方法中加入判斷,
class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.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
__getitem__()中檢測到以切片方式訪問時,獲取切片開始和結束位置,計算開始至結束位置范圍對應的斐波拉契陣列值,并將其放入到一個串列中,待回圈結束后回傳這個串列,
測驗結果:
>>> fib = Fib() >>> fib[4] 5 >>> fib[:5] [1, 1, 2, 3, 5]
7、__getattr__方法
一般情況下,如果我們訪問到一個物件中不存在的成員屬性時,會發生例外
def student(object): def __init__(self, _name): self.name = _name
>>> stu = student('Li Ming') >>> print(stu.name) Li Ming >>> print(stu.age) Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> print(stu.age) AttributeError: 'student' object has no attribute 'age'
很明顯,這是因為類中沒有age這個屬性引起的,除了在類中添加age屬性外,Python中還提供了另外一種機制,在類實作__getattr__方法,動態回傳一個屬性,
>>> stu = student('Li Ming') >>> print(stu.age) 100
小結
這里僅介紹的最常用的幾個“魔術方法”,通過例子演示,我們可以看到Python編程時使用“魔術方法”去定制自己類的優勢所在,Python中還有很多“魔術方法”可供我們去使用,后續接觸到時再對本文的內容進行補充!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/242690.html
標籤:Python
上一篇:PHP設計模式之備忘錄模式
