1. 程式人生 > >4.1.7 特殊方法與運算符重載

4.1.7 特殊方法與運算符重載

clas 這一 1.7 生成 tle tro .py 地址 定義類

  Python的類有大量的特殊方法,其中比較常見的是構造函數和析構函數。Python中類的構造函數是__init__(),一般用來為數據成員設置初始值或進行其他必要的初始化工作,在創建對象時被自動調用和執行。如果用戶沒有設計構造函數,Python將提供一個默認的構造函數用來進行必要的初始化工作。Python中類的析構函數是__del__(),一般用來釋放對象占用的資源,在Python刪除對象和回收對象空間時別自動調用和執行。如果用戶沒有編寫析構函數,Python將提供一個默認的析構函數進行必要的清理工作。

  在Python中,除了構造函數和析構函數之外,還有大量的特殊方法支持更多的功能,例如,運算符重載就是通過在類中重寫特殊函數來實現的。在自定義類時如果重寫了某個特殊方法即可支持對應的運算符,具體實現了什麽工作則完全可以根據需要來定義。

                          Python類特殊方法

方法 功能說明
__new__() 類的靜態方法,用於確定是否要創建對象
__init__() 構造函數,生成對象時調用
__del__() 析構函數,釋放對象時調用
__add__() +
__sub__() -
__mul__() *
__truediv__() /
__floordiv__() //
__mod__() %
__pow__() **
__repr__() 打印、轉換
__setitem__() 按照索引賦值
__getitem__()
按照索引獲取值
__len__() 計算長度
__call__() 函數調用
__contains__() in
__eq__()、__ne__()、__lt__()、__le__()、__gt__()、__ge__() ==、!=、<、<=、>、>=
__str__() 轉化為字符串
___lshift__()、__rshift__() <<、>>
__and__()、__or__()、__invert__()、__xor__() &、|、~、^
__iadd__()、__isub__()
+=、-=

1、__init__:構造初始化函數,在創建實例對象為其賦值時使用,在__new__之後,__init__必須至少有一個參數self,就是這個__new__返回的實例,__init__是在__new__的基礎上可以完成一些其它初始化的動作,__init__不需要返回值。

2、__new__:創建並返回一個實例對象,如果__new__只調用了一次,就會得到一個對象。繼承自object的新式類才有__new__這一魔法方法,__new__至少必須要有一個參數cls,代表要實例化的類,此參數在實例化時由Python解釋器自動提供,__new__必須要有返回值,返回實例化出來的實例(很重要),這點在自己實現__new__時要特別註意,可以return父類__new__出來的實例,或者直接是object的__new__出來的實例,若__new__沒有正確返回當前類cls的實例,那__init__是不會被調用的,即使是父類的實例也不行。__new__是唯一在實例創建之前執行的方法,一般用在定義元類時使用。

創建對象的步驟:

a、首先調用__new__得到一個對象

b、調用__init__為對象添加屬性

c、將對象賦值給變量。

 1 class A(object):
 2     pass
 3 
 4 class B(A):
 5     def __init__(self):
 6         print(__init__被調用...)
 7 
 8     def __new__(cls):
 9         print(__new__被調用...)
10         print(id(cls))
11         return object.__new__(A)  #註意在此處采用了參數A而不是cls,__new__沒有正確返回當前類cls的實例
12     
13 ‘‘‘
14 從運行結果可以看出,__new__中的參數cls和B的id是相同的,表明__new__中默認的參數cls就是B類本身,而在return時,
15 並沒有正確返回當前類cls的實例,而是返回了其父類A的實例,因此__init__這一魔法方法並沒有被調用,
16 此時__new__雖然是寫在B類中的,但其創建並返回的是一個A類的實例對象。
17 ‘‘‘

3、__class__:獲得已知對象的類 ( 對象.__class__)。

1 class A:
2     count = 0
3     def addcount(self):
4         self.__class__.count += 1

4、__str__:在將對象轉換成字符串 str(對象) 測試的時候,打印對象的信息,__str__方法必須要return一個字符串類型的返回值,作為對實例對象的字符串描述,__str__實際上是被print函數默認調用的,當要print(實例對象)時,默認調用__str__方法,將其字符串描述返回。如果不是要用str()函數轉換。當你打印一個類的時候,那麽print首先調用的就是類裏面的定義的__str__。
 1 class A:
 2     def __init__(self,name):
 3         self.name = name
 4 
 5     def __str__(self):
 6         return 我是A類的實例對象a,我的名字叫{}.format(self.name)
 7 
 8 if __name__ == __main__:
 9 
10     a = A(老王)
11     print(A)  #<class ‘__main__.A‘>
12     print(a)  #我是A類的實例對象a,我的名字叫老王
13 
14     #__str__方法是不會被調用的,而print(a)的時候,_str__就被調用了。

可以看出,直接敲a的話,__str__方法是不會被調用的,而print(a)的時候,__str__就被調用了。

5、__repr__:如果說__str__體現的是一種可讀性,是給用戶看的,那麽__repr__方法體現的則是一種準確性,是給開發人員看的,它對應的是repr()函數,重構__repr__方法後,在控制臺直接敲出實例對象的名稱,就可以按照__repr__中return的值顯示了。
 1 class A:
 2     def __init__(self,name):
 3         self.name = name
 4 
 5     def __str__(self):
 6         return 我是A類的實例對象a,我的名字叫{}.format(self.name)
 7 
 8     def __repr__(self):
 9         return 哈哈,我是A的實例對象a!
10 
11 if __name__ == __main__:
12 
13     a = A(老王)
14     print(A)  #<class ‘__main__.A‘>
15     print(a)  #我是A類的實例對象a,我的名字叫老王
16     print(repr(a))
17 
18 ‘‘‘
19 打印操作會首先嘗試__str__和str內置函數(print運行的內部等價形式),它通常應該返回一個友好的顯示。
20 
21 __repr__用於所有其他的環境中:用於交互模式下提示回應以及repr函數,它通常應該返回一個編碼字符串,可以用來重新創建對象,或者給開發者詳細的顯示。
22 
23 當我們想所有環境下都統一顯示的話,可以重構__repr__方法;當我們想在不同環境下支持不同的顯示,例如終端用戶顯示使用__str__,而程序員在開發期間則使用底層的__repr__來顯示,實際上__str__只是覆蓋了__repr__以得到更友好的用戶顯示。
24 
25 ‘‘‘


6、__del__:對象在程序運行結束之後進行垃圾回收的時候調用這個方法,來釋放資源。此時,此方法是被自動調用的。除非有特殊要求,一般不要重寫。在關閉數據庫連接對象的時候,可以在這裏,釋放資源。
 1 class NewClass(object):
 2     num_count = 0  # 所有的實例都共享此變量,即不單獨為每個實例分配
 3     def __init__(self,name):
 4         self.name = name
 5         NewClass.num_count += 1
 6         print(name,NewClass.num_count)
 7 
 8     def __del__(self):
 9         NewClass.num_count -= 1
10         print(Del,self.name,NewClass.num_count)
11 
12     def test(self):
13         print(aa)
14 
15 if __name__ == __main__:
16     #aa = NewClass(‘Hello‘)
17     #bb = NewClass(‘World‘)
18     #cc = NewClass(‘aaaa‘)
19     #print(‘Over‘)
20     pass
21 
22 #可以看出在程序運行結束之後,__del__默認被調用了三次,分別對實例對象aa,bb,cc進行垃圾回收,因為此時創建的實例已經沒有對象再指向它了。
23 
24 import time
25 
26 class Animal:
27     def __init__(self,name):
28         print(__init__方法被調用)
29 
30     def __del__(self):
31         print(__del__ ......)
32 
33 wangcai = Animal(旺財)
34 xiaoqiang = wangcai
35 
36 del wangcai
37 print(* * 50)
38 del xiaoqiang
39 time.sleep(2)
40 print(over......)
41 
42 #__init__方法被調用
43 #**************************************************
44 #__del__ ......
45 #over......
46 
47 ‘‘‘可以看出,wangcai和xiaoqiang指向的是同一個實例對象,在del wangcai的時候,__del__並沒有被調用,因為此時這個對象還在被xiaoqiang引用著,當del xiaoqiang的時候,__del__就默認被調用了,因為此時沒有變量再引用這個實例對象了,相當於其引用計數變為0了,這個對象理所當然就會被垃圾回收。
48 
49 總而言之,__del__魔法方法是在對象沒有變量再引用,其引用計數減為0,進行垃圾回收的時候自動調用的。
50 
51 ‘‘‘

7、__getattribute__:屬性訪問攔截器,在訪問實例屬性時自動調用。在python中,類的屬性和方法都理解為屬性,且均可以通過__getattribute__獲取。當獲取屬性時,相當於對屬性進行重寫,直接return object.__getattribute__(self, *args, **kwargs)或者根據判斷return所需要的重寫值,如果需要獲取某個方法的返回值時,則需要在函數後面加上一個()即可。如果不加的話,返回的是函數引用地址。
 1 class Test(object):
 2     def __init__(self,subject1):
 3         self.subject1 = subject1
 4         self.subject2 = cpp
 5 
 6     def __getattribute__(self,obj):
 7         if obj == subject1:
 8             return redirect python
 9         else:
10             return object.__getattribute__(self,obj)
11 
12 
13 if __name__ == __main__:
14     s = Test(Python)
15     print(s.subject1)
16     print(s.subject2)
17 
18 #redirect python
19 #cpp
20 
21 ‘‘‘
22 在創建實例對象s並對其初始化的時候,subject1的值設置為‘python’,subject2的值設置為‘cpp’,
23 在訪問s的subject1屬性時,因為Test類對object類中的__getattribute__方法進行了重寫,
24 所以在調用此方法時,首先對要訪問的屬性做一個攔截和判斷,
25 此時__getattribute__方法中的參數obj對應的是要訪問的屬性,若要訪問subject1屬性,
26 則對該屬性進行重寫,返回了一個不同的字符串,我們可以看到,在初始化時,
27 subject1 的值為‘python’,而在訪問subject1這個屬性時,返回的值是‘redirect python‘,
28 而在訪問subject2時,則調用其父類中的__getattribute__方法,返回正常的subject2屬性的值。
29 當然,在訪問類的方法屬性時,也可以通過重寫__getattribute__的方法對其進行重寫。
30 ‘‘‘

8、__bases__:獲取指定類的所有父類構成元素,使用方法為類名.__bases__

 1 class A:
 2     pass
 3 
 4 class B(A):
 5     pass
 6 
 7 class C():
 8     pass
 9 
10 class D(B,C):
11     pass
12 
13 print(D.__bases__)
14 
15 #(<class ‘__main__.B‘>, <class ‘__main__.C‘>)   直顯示父類,不現實父類的父類

9、__mro__:顯示指定類的所有繼承脈絡和繼承順序,假如這個指定的類不具有某些方法和屬性,但與其有血統關系的類中具有這些屬性和方法,則在訪問這個類本身不具有的這些方法和屬性時,會按照__mro__顯示出來的順序一層一層向後查找,直到找到為止。
 1 class A:
 2     pass
 3 
 4 class B(A):
 5     pass
 6 
 7 class C():
 8     pass
 9 
10 class D(B,C):
11     pass
12 
13 print(D.__mro__)
14 
15 # (<class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.A‘>, 
<class ‘__main__.C‘>, <class ‘object‘>)

10、__call__:具有__call__魔法方法的對象可以使用XXX()的形式被調用,比如說類的實例對象
 1 class Dog(object):
 2 
 3     def __init__(self):
 4         print(__init__被調用......)
 5 
 6     def __call__(self):
 7         print(__call__被調用......)
 8 
 9 #wangcai = Dog()
10 #wangcai()
11 
12 ‘‘‘
13 __init__被調用......
14 Traceback (most recent call last):
15   File "C:/Users/dddd/PycharmProjects/untitled5/test1.py", line 8, in <module>
16     wangcai()
17 TypeError: ‘Dog‘ object is not callable
18 ‘‘‘
19 ‘‘‘
20 可以看到,Dog類的實例對象laowang是不可以使用laowang()的方式進行調用的,
因為其沒有__call__魔法方法,進行了修改之後,
laowang這個實例對象就可以使用()的方式被調用了:
21 ‘‘‘ 22 23 #然後為Dog類重寫 __call__函數 24 wangcai = Dog() 25 wangcai() 26 27 #__init__被調用...... 28 #__call__被調用......

11、魔法屬性:__stlos__:可以限制實例對象的屬性和方法,但是對類不起作用。

12、__all__:將一個py文件作為模塊導入時,其中if __name__ == "main"以上的類、方法、函數等都能被導入,但某些方法可能只是用來做測試用的,不希望也不建議被導入,可以用__all__=[‘函數名或方法名‘]的方式限制一下哪些函數或方法可以被導入,即[]中的函數名或方法名可以被導入。但是需要強調的是,__all__魔法方法只針對通過 from xx import *這種導入方式有效
 1 #自定義一個腳本
 2 __all__ = [a,b]
 3 
 4 def a():
 5     print(a)
 6     
 7 def b():
 8     print(b)
 9 
10 def c():
11     print(c)
12     
13 if __name__ == __main__:
14     print(hello 老王)
15     print(123)

https://www.jianshu.com/p/3f4786b33f34

4.1.7 特殊方法與運算符重載