python magic method | 神奇的雙下劃綫方法

「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!前端

微信公衆號搜索【程序媛小莊】,關注半路出家的程序媛如何靠python開發養家餬口~python

前言

本文介紹一下python中經常使用的魔法方法以及面向對象中很是重要的單例模式。編程

魔法方法

python中一切皆對象,由於python是面向對象的編程語言。python給類和對象提供了大量的內置方法,這些內置方法也稱魔法方法。這些魔法方法老是在某種條件下自動觸發執行,就像魔法同樣。後端

__init__方法

該方法是用來接收定義類時類中__new__方法返回的空對象後爲空對象進行初始化的操做,沒有返回值。設計模式

class Test():
    def __init__(self, name):
        self.name = name
        
    def test(self):
        print(self.name)
   
t = Test('xu')
t1 = Test('python')
複製代碼

__new__方法

該方法是當類被調用實例化對象時首先被觸發的方法,用來實例化一個空對象並返回。微信

class Test():
    def __new__(cls,*args, **kwargs):
        return object.__new__(cls, *args, **kwargs) 
    
    def __init__(self, name):
        self.name = name
複製代碼

__call__方法

若是想讓一個對象變成一個可調用對象(加括號能夠調用),須要在該對象的類中定義__call__方法,調用可調用對象的返回值就是__call__方法的返回值。markdown

class Test():
    
    def __init__(self):
        self.name = 'python'
    
    def __call__(self, *args, **kwargs):  # self是Test類的對象
        print(self)  # <__main__.Test object at 0x000001C78CE78FD0>
        print(self.name)
        
t = Test()
t()  # python
複製代碼

__str___方法

當對象被訪問打印時觸發執行,該方法必須有一個字符串類型的返回值。編程語言

class Test():
    def __init__(self, name):
        self.name = name
	
    def __str__(self):
        return self.name
   
t = Test('xu')
print(t1)  # xu
複製代碼

__del___方法

__del__方法是在對象被刪除時自動觸發,因爲python的垃圾回收機制會自動清理程序中沒用的資源,所以若是一個對象只是佔用應用程序的資源,沒有必要定義__del__方法,可是若是設計到佔用系統資源的話好比打開的文件對象,因爲關係到操做系統的資源,python的垃圾回收機制派不上用場的時候,就須要爲對象建立__del__方法,用於對象被刪除後自動觸發回收操做系統資源。svn

class Test:
    def __init__(self):
        self.x = open('a.txt',mode='w')
        # self.x = 佔用的是操做系統資源

    def __del__(self):
        print('run')
        # 發起系統調用,告訴操做系統回收相關的系統資源
        self.x.close()

obj = T()
del obj # obj.__del__() 
複製代碼

__enter__ & __exit__方法

使用with上下文管理時,會觸發對象中的__enter__方法,並將__enter__方法的返回值賦值給as聲明的變量。oop

with語句正常結束的時候會觸發__exit__方法,該方法的三個參數分別表明異常類型、異常值和溯源信息,若是with語句代碼塊出現異常,則with語句後的代碼都不會被執行,可是若是該方法返回值爲True,異常會被清空,with代碼塊後的代碼還會被正常執行。代碼以下:

class Open:
    def __init__(self):
        self.name = 'open'

    def __enter__(self):
        print('with語句執行時會首先執行的方法,返回值會賦值給as聲明的變量')
        return self.name

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中的代碼塊執行完畢時執行exit')
        print(exc_type, '若是出現異常表示異常類型')
        print(exc_val, '表示異常的值')
        print(exc_tb, '表示異常的溯源信息')
        return 123  # 非零 非空 非None爲真

    
with Open() as test:
    print(test)
    raise TypeError('看一下錯誤信息')
print('我會不會被執行呢')  # 當__exit__方法返回值爲真時,會被執行,不然不會被執行
複製代碼

item系列方法

item系列方法包括__setitem__、__getitem__、delitem__方法,這三種方法分別會在中括號賦值/修改值、中括號取值、中括號刪除值時觸發,好比能夠自定義一個字典類,並自定義中括號賦值、取值、刪除值的方法:

class MyDict(dict):

    def __setitem__(self, key, value):
        print('執行setitem', key, value)  # 執行setitem, x, 1
        self.__dict__[key] = value

    def __getitem__(self, item):
        print('執行getitem', item)  # 執行getitem x
        print(self.__dict__[item])  # 1

    def __delitem__(self, key):
        print('執行delitem', key)  # 執行delitem x
        self.__dict__.pop(key)


d = MyDict()
d['x'] = 1
print(d['x'])
del d['x']
複製代碼

attr系列方法

attr系列方法包括__setattr__,__getattr__,__delattr____setattr__在添加/修改屬性時會觸發,___delattr__刪除屬性的時候觸發,__getattr__在使用.調用屬性而且屬性不存在時觸發。以下代碼所示

class Test:
    def __init__(self):
        self.name = 'python'

    def __setattr__(self, key, value):
        print('添加/修改屬性setattr')
        self.__dict__[key] = value
        # self.key = value # 會出現無線遞歸,由於對象.屬性會調用__setattr__方法

    def __delattr__(self, item):
        print('刪除屬性delattr')
        self.__dict__.pop(item)

    def __getattr__(self, item):
        print('屬性不存在時調用getattr')


t = Test()
t.x = 'x'
print(t.y)
del t.x
複製代碼

單例模式

單例模式是一種軟件設計模式,爲了保證一個類不管調用多少次產生的對象都指向同一個內存地址,即僅僅只有一個對象。

實現單例模式的方式有不少,總的原則就是保證一個類只要實例化一個對象,所以關鍵點就是如何判斷這個類是否實例化過一個對象。

這裏介紹幾種實現方式,供你們參考:

模塊導入的方式

這種方式的原理是模塊導入後只運行一次,後面再次使用該模塊中的類是直接從內存中查找。

# cls_singleton.py
class Foo(object):
    pass

instance = Foo()

# test.py
import cls_singleton

obj1 = cls_singleton.instance
obj2 = cls_singleton.instance
print(obj1 is obj2)  # True
複製代碼

經過__new__方法

原理就是判斷類是否有實力,有就直接返回,沒有就保存到_instance

class Test:

    _instance = None

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __new__(cls, *args, **kwargs):
        # if cls._instance:
        # return cls._instance # 有實例則直接返回
        # else:
        # cls._instance = super().__new__(cls) # 沒有實例則new一個並保存
        # return cls._instance # 這個返回是給是給init,再實例化一次,也沒有關係

        if not cls._instance:	                        # 這是簡化的寫法,上面註釋的寫法更容易提現判斷思路
            cls._instance = super().__new__(cls)
        return cls._instance


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True
複製代碼

自定義元類的方式

這種方式的原理是類調用的過程,類定義時會調用元類下的__init__,類調用(實例化對象)時會觸發元類下的__call__方法。

class Mymeta(type):

    def __init__(cls, name, bases, dic):
        super().__init__(name, bases, dic)
        cls._instance = None		                  # 將記錄類的實例對象的數據屬性放在元類中自動定義了

    def __call__(cls, *args, **kwargs):	                  # 此call會在類被調用(即實例化時觸發)
        if cls._instance:				  # 判斷類有沒有實例化對象
            return cls._instance
        else:						  # 沒有實例化對象時,控制類造空對象並初始化
            obj = cls.__new__(cls, *args, **kwargs)
            obj.__init__(*args, **kwargs)
            cls._instance = obj			          # 保存對象,下一次再實例化能夠直接返回而不用再造對象
            return obj


class Test(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True
複製代碼

結語

文章首發於微信公衆號程序媛小莊,同步於掘金知乎

碼字不易,轉載請說明出處,走過路過的小夥伴們伸出可愛的小指頭點個贊再走吧(╹▽╹)

wallhaven-z8ykry.jpg

相關文章
相關標籤/搜索