摘要:魔法方法類構造方法魔法方法初始化對象創建對象的過程創建一個對象解釋器會自動的調用方法返回創建的對象的引用,給實例實例化執行該方法,返回值。當引用計數為時,該對象生命就結束了。
define class
class的三個組成部分:
類的名稱:類名
類的屬性: 一組數據
類的方法:允許對進行操作的方法(行為)
定義
class Student (object): pass
class后面定義類名(類名通常是大寫開頭的單詞)
(object),表示該類是從哪個類繼承下來的
實例化
創建實例是通過類名+()實現
stu = Student()
class Stu (): # 定義class age = 10 # 屬性 def show (self): # 方法 print(self.age) # 類中獲取屬性 print(self, "self") print(stu.name) # 獲取類外添加屬性 stu = Stu() # 實例化 stu.name = "sf" # 添加屬性 stu.show() # 調用方法self
self當前實例化的對象
在定義函數的時候,第一個參數需要self
class Stu (): def show_name (self): print(self.name) stu = Stu() stu.name = "sf" stu.show_name()
self在定義時需要定義,但是在調用時會自動傳入。
self的名字并不是規定寫死的,但是最好還是按照約定是self。
self總是指調用時的類的實例。
init魔法方法:
["__class__", "__delattr__", "__dict__", "__dir__", "__doc__", "__eq__", "__format__", "__ge__", "__getattribute__", "__gt__", "__hash__", "__init__", "__init_subclass__", "__le__", "__lt__", "__module__", "__ne__", "__new__", "__reduce__", "__reduce_ex__", "__repr__", "__setattr__", "__sizeof__", "__str__", "__subclasshook__", "__weakref__"]
__init__: 類構造方法(魔法方法)
class Stu (): # 初始化對象 def __init__ (self, new_name, new_age): self.name = new_name self.age = new_age def show (self): print("name: %s, age: %d" % (self.name, self.age)) stu = Stu("sf", 23) stu.show()
創建對象的過程:
創建一個對象
Python解釋器會自動的調用__init__方法
返回創建的對象的引用,給實例
__str__: 實例化執行該方法,返回值。
當需要print一個類的時候,需要先在類中定義__str__方法,返回值,就是print()輸出的值
class Stu (): def __init__ (self, new_name): self.name = new_name def __str__ (self): return self.name
__new__: 方法主要是當繼承一些不可變的class時(比如int, str, tuple), 提供一個自定義這些類的實例化過程.
class Stu(object): def __new__(cls): return object.__new__(cls) # 自定義實例化過程 # 自身沒有能力創建實例,可以讓父類創建 def __init__(self): print("init") stu = Stu()
創建單例對象
class Single(object): __instance = None def __new__(cls): if cls.__instance != None: return cls.__instance else: cls.__instance = object.__new__(cls) return cls.__instance s1 = Single() s2 = Single()私有方法和私有屬性
私有屬性
按照約定俗成的規定__開頭的屬性表示私有屬性, 不可以直接類名.變量名訪問
在類中存儲的形式為:_Stu__age, _類名__變量名
class Stu(): def __init__(self, new_name): self.name = new_name self.__age = 0 # 定義了一個私有的屬性,屬性的名字是`__age`
在類中訪問形式:self.__變量名
私有方法(private)
按照約定俗成的規定__開頭的屬性表示私有方法, 不可以直接類名.方法名訪問
存儲的形式為:_Stu__get_age, _類名__方法名
class Stu(): def __test(self): # 定義私有方法 pass
在類中調用私有方法:self.__方法名()
有些時候,會看到以一個下劃線開頭的實例變量名,比如_name,這樣的實例變量外部是可以訪問的,但是,按照約定俗成的規定,當看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。
del
__del__: 當刪除一個對象時,python解釋器會默認調用一個魔術方法,__del__()
class Stu(): def __del__ (self): print("remove obj") stu = Stu() del stu
在類的生命周期中,如果類銷毀了,python會自動調用__del__方法。也就是說,不管是手動調用del還是由python自動回收都會觸發__del__方法執行。
對象引用個數
模塊sys中有一個getrefcount方法可以測試對象的引用個數
返回的結果,會比實際結果大1.
import sys sys.getrefcount("變量/方法")繼承
class Animal(object): def run(self): print("Animal is running") class Dog(Animal): # 繼承 def run(self): print("Dog is running") class Cat(Animal): pass dog = Dog() cat = Cat() dog.run() cat.run()
當子類和父類都存在相同的run()方法時,子類的run()覆蓋了父類的run(),在代碼運行的時候,總是會調用子類的run() -- 多態
重寫
重寫父類的方法,繼承之后,子類定義和父類方法名一樣的方法
class Animal(object): def run(self): print("Animal is running") class Dog(Animal): def run(self): # 重寫 print("Dog is running") class Cat(Animal): pass dog = Dog() cat = Cat() dog.run() cat.run()
調用父類方法
類名調用
super關鍵字調用
class Animal(): def run(self): print("Animal is running") class Dog (Animal): def say(self): # 第一種,類名調用。 # Animal.run(self) #方法必須傳遞參數`self` # 第二種,super關鍵字 super().run() print("Gog is running") dog = Dog() dog.say()
私有方法,私有屬性在繼承中的表現:
私有方法并不會被繼承(子類外和子類內都不會讓使用)
私有屬性并不會被繼承(子類外和子類內都不會讓使用)
多繼承
子類具有多個父類
class A(object): # object是所有最終類的終點 def test(self): print("A") class B: def test(self): print("B") class C(A, B): # 多繼承 (如果繼承類中方法名或者屬性名相同,生效的是參數先后順序,`類名.__mro__`中的順序) pass print(C.__mro__) # (多態, , , ) # C3算法 c = C() # 子類也會重寫父類的方法
定義時的類型和運行時的類型不一樣。
執行的時候確定
class D(object): def _print(self): print("D") class X(D): def _print(self): print("X") def introduce(temp): temp._print() d = D() x = X() introduce(d) introduce(x)
isinstance()判斷一個對象是否是某種類型
實例屬性屬于各個實例所有,互不干擾;
類屬性屬于類所有,所有實例共享一個屬性;
不要對實例屬性和類屬性使用相同的名字,否則將產生難以發現的錯誤。
類屬性 & 實例屬性
class A(): num = 1 # 類屬性 def __init(self): print(self.num) a = A() a.name = 100 # 實例屬性 print(A.num) # 獲取類屬性
實例方法 & 類方法 & 靜態方法
class A(): # 定義類方法 @classmethod def add_num(cls): # 保存類的引用 pass def get_num(self): # 實例方法 pass @staticmethod def set_num(): # 靜態方法 # 可以沒有任何參數引用 pass a = A() A.add_num() # 調用類方法 # a.add_num() # 實例調用類方法 A.set_num() # 調用靜態方法 a.set_num() # 實例調用靜態方法
私有化
xx: 公有變量
_x: 單前置下劃線,私有化屬性或方法,from somemodule import *禁止導入,類對象和子類可以訪問
__xx: 雙前置下劃線,避免與子類中的屬性名命名沖突,無法在外部直接訪問
__xx__: 雙前后下劃線,命名空間的魔法對象或屬性。__init__。(開發時,不要使用這種定義變量方式)
xx_: 單后置下劃線,用于避免與Python關鍵詞的沖突
property
作用:獲取與設置自動調用方法
類屬性方式調用property:
class Money(object): def __init(self): pass def getMoney(self): pass def setMoney(self, value): pass money = property(getMoney, setMoney) # key 為調用時候的key m = Money() m.money = 10
裝飾器方式使用property:
class Money(object): def __init__(self): self.__num = 0 @property def money(self): # 函數名和調用key對應 # 獲取 return self.__money @money.setter def money(self, value): # 獲取 self.__num = value m = Money() print(m.money)垃圾回收
小整數對象池
作用:為了優化速度,使用了小整數對象池,避免為整數頻繁申請和銷毀內存空間。
Python對小整數的定義是[-5, 257)這些整數對象都是提前建立好的,不會被垃圾回收。在一個Python的程序中,所有位于這個范圍內的整數使用的都是同一個對象。
單個字母也是這樣,但是定義2歌相同字符串時,引用計數為0,觸發垃圾回收。
小整數[-5,257)共用對象,常駐內存
單個字符共用對象,常駐內存
大整數對象池
每一個大整數,均創建一個新的對象。
大整數不共用內存,引用計數為0,銷毀
數值類型和字符串類型在Python中都是不可變的,意味著無法修改這個對象的值,每次對變量對修改,實際上是創建一個新的對象
intern機制
a1 = "HelloWorld" a2 = "HelloWorld" a3 = "HelloWorld"
intern機制,讓它只占用一個”HelloWorld”所占的內存空間。靠引用計數去維護何時釋放。
單個單詞,不可修改,默認開啟intern機制,共用對象,引用計數為0,則銷毀
字符串(含有空格),不可修改,沒開啟intern機制,不共用對象,引用計數為0,銷毀
Garbage collection(GC垃圾回收)
Python采用對是引用計數機制為主,標記-清除和分代收集兩種機制為輔的策略
引用計數機制
Python里每一個東西都是對象,它們的核心就是一個結構體: PyObject
typedef struct_object { int ob_refcnt; // 引用計數 struct_typeobject *ob_type; } PyObject;
PyObject是每個對象必有的內容,其中ob_refcnt就是作為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少。
當引用計數為0時,該對象生命就結束了。
引用計數機制的優點:
簡單
實時性: 一旦沒有引用,內存就直接釋放了。不用像其它機制等待特定實際。
處理回收內存的時間分攤到了平時。
引用計數機制的缺點:
維護引用計數消耗資源
循環引用
垃圾回收機制
Python中的垃圾回收是以引用計數為主,分代收集為輔
導致引用計數+1的情況
對象被創建,例如: a = 23
對象被引用,例如: b = a
對象被作為參數,傳入到一個函數中,例如: func(a)
對象作為一個元素,存儲在容器中,例如: list1 = [a,a]
導致引用計數-1的情況
對象的別名被顯式銷毀,例如: del a
對象的別名被賦予新的對象,例如: a = 24
一個對象離開它的作用域,例如f函數執行完畢時,func函數中的局部變量(全局變量不會)
對象所在的容器被銷毀,或從容器中刪除對象
查看一個對象的引用計數
import sys a = "hello world" sys.getrefcount(a)
可以查看a對象的引用計數,但是比正常計數大1,因為調用函數的時候傳入a,這會讓a的引用計數+1
循環引用導致內存泄露
引用計數的缺陷是循環引用的問題
import gc class ClassA(): def __init__(self): print("object born,id:%s"%str(hex(id(self)))) def f2(): while True: c1 = ClassA() c2 = ClassA() c1.t = c2 c2.t = c1 del c1 del c2 # 把python的gc關閉 gc.disable() f2()
執行f2(),進程占用的內存會不斷增大。
創建了c1, c2后這兩塊內存的引用計數都是1,執行c1.t = c2和c2.t = c1后,這兩塊內存的引用計數變成2.
在del c1后,內存1的對象的引用計數變為1,由于不是為0,所以內存1的對象不會被銷毀,所以內存2的對象的引用數依然是2,在del c2后,應用計數也是1,內存1的對象,內存2的對象的引用數都是1.
雖然他們兩個對象都是可以被銷毀的,但是由于循環引用,導致垃圾回收器不會回收它們,就導致內存泄漏.
垃圾回收
垃圾回收 = 垃圾檢查 + 垃圾回收
有三種情況會觸發垃圾回收:
調用gc.coolect()
當gc模塊的計數器達到閥值的時候
程序退出的時候
gc模塊
gc模塊作用:解決循環引用的問題
常用函數:
gc.set_debug(flags)設置gc的debug日志。一般設置為gc.DEBUG_LEAK
gc.collect([generation]) 顯式進行垃圾回收,可以輸入參數,0表示只檢查第一代的對象,1代表檢查一,二代對象,2代表檢查一,二,三代的對象。如果不傳入參數,執行一個full collection,也就是等于2,返回不可達(unreachable objects)對象的數目
gc.get_threshold()獲取的gc模塊中自動執行垃圾回收的頻率
gc.set_threshold(threshold0[, threshold1[, threshold2])設置自動執行垃圾回收的頻率
gc.get_count()獲取當前自動執行垃圾回收的計數器,返回一個長度為3的列表
Note:
gc模塊唯一處理不了的是循環引用的類都有__del__方法,項目中要避免定義__del__方法。
class ClassA(): pass # def __del__(self): # print("object born,id:%s"%str(hex(id(self))))
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41387.html
閱讀 5257·2021-09-22 15:50
閱讀 1862·2021-09-02 15:15
閱讀 1164·2019-08-29 12:49
閱讀 2543·2019-08-26 13:31
閱讀 3458·2019-08-26 12:09
閱讀 1210·2019-08-23 18:17
閱讀 2736·2019-08-23 17:56
閱讀 2929·2019-08-23 16:02