日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python基础教程:类的特殊成员及高级特性

發布時間:2025/3/20 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python基础教程:类的特殊成员及高级特性 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 對象的格式化輸出

(1)如果需要對一個對象(實例)進行格式化輸出,可以重寫類的__repr__()和__str__()方法。

兩者的區別:使用交互式解釋器輸出對象時,結果是__repr__()方法返回的字符串;使用 str() 或 print() 函數會輸出__str__()方法返回的字符串。

參見下例:

class Point:"""二維坐標系中的點"""def __init__(self, x, y):self.x = xself.y = ydef __repr__(self):return "Point({0.x!r}, {0.y!r})".format(self)def __str__(self):return "({0.x!s}, {0.y!s})".format(self)

在交互式命令行【ipython】中的結果:

In [2]: point = Point(1, 2)In [3]: point Out[3]: Point(1, 2)In [4]: print(point) (1, 2)

可以看到,在交互式環境中格式化輸出對象是Point(1, 2);而通過print()打印出的對象是(1, 2)。

(2) 注意,在格式化中使用 !r 表示輸出使用 __repr__()來代替默認的__str__()。

In [5]: print("point is {!r}".format(point)) point is Point(1, 2)In [6]: print("point is {!s}".format(point)) point is (1, 2)In [7]: print("point is {}".format(point)) point is (1, 2)

如果__str__()沒有被定義,會使用__repr__()來代替輸出。通常來講自定義__repr__()和 __str__()是很好的習慣,因為它能簡化調試和實例輸出。

2 獲取類的描述

可以通過__doc__這個特殊字段獲取類的描述【即類的注釋】,用法 類名.__doc__,參見下列示例:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:"""description..."""def func(self):passprint(A.__doc__) # description...

3 獲取類或對象的所有成員

可以通過__dict__獲取到類或對象的所有成員信息(字典形式),用法 類名.__dict__或者對象.__dict__,參見下例:

class A:def __init__(self, name):self.name = namedef func(self):pass# 獲取類的所有成員 print(A.__dict__) #{'__module__': '__main__', 'func': <function A.func at 0x00000000025976A8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}# 獲取對象的所有成員 obj = A("Liu You Yuan") print(obj.__dict__) # {'name': 'Liu You Yuan'}

可以看到類與對象成員之中,只有【普通字段】是存儲在對象中的,其他成員都是在類中。

4 獲取創建當前操作的對象的類名

通過__class__能夠獲取當前操作的對象是由哪個類所創建,用法對象.__class__,參見下例:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:def func(self):passobj = A()# 獲取 [當前操作的對象] 所在的類名 print(obj.__class__) # <class '__main__.A'>

5 獲取創建當前操作的對象的類所在的模塊名

通過__module__能夠獲取創建當前操作的對象的類所在的模塊,用法對象.__module__,參見下例:

class A:def func(self):passobj = A() # 獲取 [當前操作的對象] 所在的模塊名 print(obj.__module__) # __main__

6 讓對象可迭代

只需要在類中實現__iter__()方法,即可讓對象作用于for循環。參見如下例子:

class A:def __init__(self, lis):self.lis = lisdef __iter__(self):return iter(self.lis)obj = A([2018, 0, 3, 1, 9]) for i in obj: # 當對象用于迭代時,實際是相當于迭代__iter__方法的返回值。print(i)# 2018 # 0 # 3 # 1 # 9

上例中,如果沒有實現__iter__()方法,對象obj是不能被循環的。實際上,像list、dict、str等數據結構之所能夠被迭代,就是其類中實現了__iter__()方法。

7 讓對象支持字典操作

實現__getitem__ / __setitem__ / __delitem__, 可實現對象類似字典的操作,參見下例:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Person:def __init__(self, name):self.name = namedef __getitem__(self, k):return self.namedef __setitem__(self, k, v):self.name = vdef __delitem__(self, k):del self.nameobj = Person("Jeo Chen")result = obj['name'] # 自動觸發執行 __getitem__ print(result) # Jeo Chenobj['name'] = 'Liu You Yuan' # 自動觸發執行 __setitem__ print(obj['name']) # Liu You Yuandel obj['name'] # 自動觸發執行 __delitem__

8 優化大量對象占用的內存

如果需要創建大量(成千上萬)的對象,導致很占內存,可以通過特殊的靜態字段__solts__來減少對象所占用的內存。

下例將對比定義__solts__和 沒有定義__solts__的兩個類在創建大量對象時占用的內存大小,其中用了【反射的知識】和 【tracemalloc包】。

tracemalloc包是跟蹤由Python分配的內存塊的調試工具。其中:
(1)tracemalloc.start()方法表示開始跟蹤Python內存分配,開始時內存占用設為1;tracemalloc.stop()表示停止跟蹤;
(2)tracemalloc.get_traced_memory()方法能獲取由 tracemalloc 模塊跟蹤的內存塊的當前大小和峰值大小作為元組:(current: int, peak: int),單位為字節。

詳細參見下例:

import tracemallocITEM_NUM = 10 class HaveSlots:__slots__ = ['item%s' % i for i in range(ITEM_NUM)]def __init__(self):for i in range(len(self.__slots__)):setattr(self, 'item%s' % i, i)class NoSlots:def __init__(self):for i in range(ITEM_NUM):setattr(self, 'item%s' % i, i)# 開始跟蹤 tracemalloc.start()obj = [NoSlots() for i in range(100)] # 獲取由 tracemalloc 模塊跟蹤的內存塊的當前大小和峰值大小作為元組:(current: int, peak: int) print(tracemalloc.get_traced_memory())# 停止跟蹤 tracemalloc.stop()# 又開始跟蹤,相當于重置 tracemalloc.start() obj2 = [HaveSlots() for i in range(100)] print(tracemalloc.get_traced_memory())# (21832, 22219) # 未定義__slots__字段,創建100個對象占用的內存約為 21832 字節 # (13760, 14147) # 定義__slots__字段,創建100個對象占用的內存約為 13760 字節

上例可見,當定義了__slots__字段時, 創建大量對象所占用的內存(13760) 明顯小于 沒有定義__slots__字段的內存(21832)。或許,你得到的占用內存大小與我得到的不一致,但不影響最終結論。

__slots__究竟做了什么來降低內存呢?

(1)默認情況下,自定義的對象都使用dict來存儲屬性(通過obj.__dict__查看),而python中的dict的底層需要考慮“降低hash沖突”,因此dict所占存儲空間要比實際存儲的元素大,會浪費一定的空間。
(2)使用__slots__后的類所創建的對象只會用到這些_slots__定義的字段,也就是說,每個對象都是通過一個很小的固定大小的數組來構建字段,而不是字典。
需要注意的是:
(1)如果聲明了__slots__,那么對象就不會再有__dict__屬性。
(2)使用__slots__意味著不能再給實例添加新的屬性,只能使用在__slots__中定義的那些屬性名。
(3)定義了__slots__后的類不再支持一些普通類特性了,比如多繼承。
因此,如果需要創建成千上萬的對象,__slots__比較適用;其他情況,還是要減少對__slots__使用的沖動。

9 構造方法

類中的__init__()方法就是類的構造方法了,通過類創建對象時,自動觸發執行。參見下例:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:def __init__(self, name):self.name = nameprint("this is __init__")# 創建對象則自動觸發__ini__方法。 obj = A("Jeo Chen") # this is __init__

直到此時,才介紹構造方法其實是為后面的內容鋪墊。這里說創建對象時自動觸發執行構造方法是不準確的,繼續往下讀,會介紹__init__的真正作用。

10 析構方法

__del__方法即為類的析構方法,當對象在內存中被釋放時,自動觸發執行。不過,Python是有垃圾回收機制的高級語言,我們無需關心內存的分配和釋放。解釋器在進行垃圾回收時自動觸發執行的析構方法。

class A:def __del__(self):pass

11 __call__方法

當在對象后面加括號,即 【對象()】會自動觸發__call__方法,這一點要與構造方法相區別。構造方法是類名后面加括號,即【類名()】觸發。

class A:def __call__(self):print("this __call__")obj = A() obj() # 對象后面加括號,觸發__call__ # this __call__

12 __new__方法

實際上,在創建對象時,調用__init__方法之前,就調用了__new__方法。我們可以通過下例證明:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:def __init__(self, name):print("In A init")self.name = namedef __new__(cls, *args, **kwargs):print("In A new",)return object.__new__(cls)# 創對象 obj = A("Liu You Yuan") # In A new # In A init

那么__new__方法到底有什么作用?下例演示了不調用__init__方法創建一個對象,詳見如下:

class Person:def __init__(self, name):print("in Person init")self.name = namedef __new__(cls, *args, **kwargs):print("In Person new",)return object.__new__(cls)# 不調用 __init__() 方法來創建Person對象 obj = Person.__new__(Person) print(obj) print(obj.name)

執行結果如下:

In Person new <__main__.Person object at 0x00000000025E8EB8> Traceback (most recent call last):File "D:/githubfile/pythonclub/面向對象/new.py", line 34, in <module>print(obj.name) AttributeError: 'Person' object has no attribute 'name'

分析輸出結果:
(1)第一行打印了 “In Person new” 說明確實是調用了__new__方法;另外并沒有打印 “in Person init” 說明確實沒有調用__init__方法。
(2)第二行打印了<__main__.Person object at 0x00000000025E8EB8>, 說明確實創建了一個對象。
(3)接著有報錯,報錯內容說AttributeError: 'Person' object has no attribute 'name', 對象并沒有name屬性(字段)。

綜上,上例的結果不言而喻:

(1)__new__方法才是真正創建對象的,只不過它創建的對象在沒調用__init__前是沒有經過【初始化】的。
(2)__init__方法是初始化對象的,【初始化】的過程也就是將字段封裝到對象中,通過 對象.字段 就能訪問。

13 類是怎么產生的

常常聽到,“python 一切皆對象”, 如此,“類” 本身也是對象,既然是對象,必然有創建它的類。換言之,"類"這個對象,是由"某個特殊的類"實例化而來。這個特殊的類就是type(),又稱元類。
除了之前介紹的通過class關鍵字可以定義類,通過type()也能定義,參見下例:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def __init__(self, name):self.name = namedef hello(self):print("hello {}".format(self.name)) # 用type()定義類。第一個參數是類名,第二個參數是當前類的基類,第三個參數為類的成員 Person = type('Person', (object,), {'sayHi': hello, "__init__": __init__})obj = Person("Liu") print(obj) # <__main__.Person object at 0x0000000002368E80> obj.sayHi() # hello Liu

這樣證實了通過元類type()也能定義一個類,而且跟用class關鍵字定義的效果一樣,只不過不常用這種方式罷了。
type()和我們平常創建類和對象有什么關系呢?我們可以通過下例一探究竟:

class MyType(type):def __init__(self, child_cls, bases=None, dict=None):print("In MyType init")super(MyType, self).__init__(child_cls, bases, dict)def __new__(cls, *args, **kwargs):print("In MyTyPe new")return type.__new__(cls, *args, **kwargs)def __call__(self, *args, **kwargs):print("In MyType call")obj = self.__new__(self, args, kwargs)self.__init__(obj, *args, **kwargs)class Person(object, metaclass=MyType):def __init__(self, name):print("In Person init")self.name = namedef __new__(cls, *args, **kwargs):print("In Person new",)return object.__new__(cls)

在上例代碼中,值得注意的是:
(1)MyType繼承了type類,同時自定義了__init__ / __new__ / __call__方法。
(2)Person類中有個參數metaclass,用來指定創建Person的類, 也就是metaclass指定了由MyType這個類通過實例化,創建Person這個對象。
(3)這里多說一句,于我們而言,Person是我們定義的一個類;于MyType而言,Person是MyType創建的一個對象。

上例輸出結果如下:

In MyTyPe new In MyType init

這可以看出我們只是定義了兩個類,做了一些自定義修改。當運行上述代碼時,就已經調用了MyType類的__new__和__init__方法了,也就是這時候通過MyType已經創建好了Person這個對象了。

接著我們在上例中添加一行,再運行:

obj = Person("Liu You Yuan")

運行結果如下:

In MyTyPe new In MyType init In MyType call In Person new In Person init

分析:
(1)當執行代碼obj = Person(“Liu You Yuan”), 我們把Person看成是MyType創建的對象,那么此行代碼就是在Person對象后面加了括號,這就會觸發MyType類中的__call__方法,打印“In MyType call”;
(2)此時,__call__中的self就是Person這個對象,通過self.__new__ / self.__init__主動調用了Person中的__new__ 和__init__,這也就創建出了obj這個對象。
至此,我們知道,當我們創建一個類A,并通過類A創建對象obj時,實際上是經歷了兩個過程:
(1)通過元類type創建我們定義的類A。
(2)通過類A創建對象obj。

總結

以上是生活随笔為你收集整理的Python基础教程:类的特殊成员及高级特性的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。