Python()-類的專有方法之雙下划線方法


1. __call__() 方法

對象+() 可以直接調用__call__()方法 , 類似普通函數的調用

class CallTest(object):
    def __init__(self):
        print('I am __init__')

    def __call__(self):
        print('I am __call__')
        return True

    def run(self):
        print('I am run')
        return True


obj = CallTest()
obj.run()  # 調用普通方法        對象.func_name()
obj()  # 調用__call__()方法, 直接    對象()

打印結果:

I am __init__

I am run

I am __call__

可以看到,obj這個對象被實例化出來,如果要調用__call__方法的話,直接obj(),即可調用並返回結果。obj就類似一個函數地址,obj()即執行這個函數。

2. __init__() 方法

構造函數,在生成對象時調用

 

===========================

 

__getattr__, __setattr__, __delattr__

1. 調用對象的一個不存在的屬性時會觸發__getattr__方法 

2. 刪除對象的一個屬性的時候會觸發__delattr__方法 

3. 設置對象(增加/修改)屬性會觸發__setattr__方法

設置對象屬性和刪除對象屬性會觸發__setattr__ 和 __delattr__ 方法,但要注意的是,在調用這兩個方法時,方法內部必須操作類的屬性字典,否則會造成無限遞歸

3. __getattr__() 方法

----調用(獲取)對象屬性

class Foo:
    a = 1

    def __getattr__(self, item):
        print('run __getattr__')


f = Foo()
print(f.a)  # 屬性存在,就不會觸發__getattr__()方法
# >> 輸出: 1

print(f.b)  # 只有在使用點調用屬性且屬性不存在的時候才會觸發,並且返回None
# >> 輸出: run __getattr__
# >> 輸出: None

4. __delattr__() 方法

----刪除對象屬性

class Foo:
    def __delattr__(self, item):
        print('run __delattr__')
        # del self.item             # 這樣會造成無限遞歸
        self.__dict__.pop(item)


f = Foo()
f.a = 3
print(f.__dict__)  # >> 輸出: {'a': 3}
print(f.a)  # >> 輸出: 3
del f.a  # >> 輸出: run __delattr__
print(f.a)  # >> 報錯: AttributeError: 'Foo' object has no attribute 'a'

5. __setattr__() 方法

  ----設置屬性: 增加對象屬性, 修改對象屬性

class Foo:
    a = 1

    def __setattr__(self, key, value):
        print("run __setattr__")


f = Foo()
# 沒有賦值,什么都不會發生

f.c = 200  # 如果增加類屬性, 觸發觸發__setattr__()方法
# >> 輸出: run__setattr__

f.a = 2  # 如果修改類屬性, 觸發觸發__setattr__()方法
# >> 輸出: run __setattr__

實例化對象傳參,會觸發__getattr__方法

class Foo:
    a = 1

    def __init__(self, b):
        self.b = b  # 賦值屬性操作

    def __setattr__(self, key, value):
        print("run __setattr__")


f = Foo(100)  # 如果實例化的時候傳入參數進行賦值屬性操作,  觸發__setattr__()方法
# >> 輸出: run __setattr__

設置屬性時, 方法內部必須操作類的屬性字典

class Foo:
    a = 1

    def __setattr__(self, key, value):
        # self.key = value  # 增加/修改類屬性,會觸發__setattr__()方法,如果這個操作在setattr方法內部,會造成無限遞歸
        self.__dict__[key] = value  # 使用這種方法會完成增加/修改類屬性的操作
        print("run __setattr__")


f = Foo()
f.y = 3  # 增加/修改類屬性,調用__setattr__()方法
# >> 輸出: run __setattr__

print(f.__dict__)
# >> 輸出: {'y': 3}

當我們重寫__setattr__()方法后,方法內部如果不進行屬性字典的操作,那么除非直接操作屬性字典,否則永遠無法完成賦值

class Foo:

    def __setattr__(self, key, value):
        print("run __setattr__")


f = Foo()
f.y = 3  # 設置對象屬性,調用__setattr__()方法,而__setattr__()方法什么都沒干,所以完成不了對象的設置屬性操作
# >> 輸出: run __setattr__

print(f.__dict__)
# >> 輸出: {}

print(f.y)  # 完成不了賦值
# >> 報錯: AttributeError: 'Foo' object has no attribute 'y'

理解了__setattr__()方法的原理,我們就可以利用 __setattr__()方法 實現我們自定義的功能

class Foo:
    a = 1
    dic = {}  # 自定義一個空字典

    def __setattr__(self, key, value):
        self.dic[key] = value
        print("run __setattr__")


f = Foo()
f.y = 3
# >> 輸出: run __setattr__

print(f.dic)  # 給類變量dic添加鍵值對
# >> 輸出: {'y': 3}

print(f.__dict__)  # 類屬性不發生變化
# >> 輸出: {}

一個小示例:

class Foo:
    def __init__(self, dic):
        self._dic = dic

    def __getattr__(self, item):
        val = self._dic[item]
        if isinstance(val, dict):
            a = Foo(val)
            return a  # 重點: 又返回一個對象
        else:
            return val


dic = {'k1': 'v1', 'k2': 'v2'}
dic = Foo(dic)

print(dic.k1)
# >>輸出: v1

print(dic.k2)
# >>輸出: v2

dic = {'k1': {'k2': 'v2'}}
dic = Foo(dic)
print(dic.k1)
# >>輸出: 一個對象 <__main__.Foo object at 0x00000000024D7F98>

print(dic.k1.k2)  # 對象可以繼續點(.)取屬性操作
# >>輸出: v2

原理:
  Foo(dic)實例化一個對象, dic.k1觸發__getattr__()方法, val={'k2': 'v2'},當val值為一個字典對象時,if條件成立, 返回一個以val字典為參數的對象,就是說: dic.k1 == Foo({'k2': 'v2'}),這個對象可以繼續通過點(.)調用對象的屬性,如果有多層嵌套,一直循環下去

接着上面的例子繼續:

def v2(arg):
    return arg


dic = {'k1': {'k2': v2}}
dic = Foo(dic)
ret = dic.k1.k2(100)
print(ret)
# >> 輸出: 100

6. __getattribute__() 方法

長得和__getattr__那么像,那么__getattribute__與之有什么關系呢?

class Foo:
    a = 1

    def __init__(self, x):
        self.x = x

    def __getattribute__(self, item):
        print('不管屬性[%s]是否存在,我都會執行' % item)


f = Foo(100)
print(f.a)
# >>輸出: 不管屬性[a]是否存在,我都會執行
# >>輸出: None

print(f.b)
# >>輸出: 不管屬性[b]是否存在,我都會執行
# >>輸出: None

print(f.x)
# >>輸出: 不管屬性[x]是否存在,我都會執行
# >>輸出: None

當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中拋出異常AttributeError

class Foo:

    def __getattr__(self, item):
        print('run __getattr__')

    def __getattribute__(self, item):
        print('不管屬性[%s]是否存在,我都會執行' % item)
        # raise AttributeError('啦啦啦啦')


f = Foo()
# print(f.a)
# >>輸出: 不管屬性[a]是否存在,我都會執行
# >>輸出: None

print(f.a)  # 打開注釋,手動拋錯: raise AttributeError('q')
# >>輸出: 不管屬性[a]是否存在,我都會執行
# >>輸出: run __getattr__
# >>輸出: None

7. super()

super 的工作原理如下:

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

其中 cls 代表類, inst 代表實例, super 函數做了兩件事:

1. 獲取實例對象 inst 的類的 MRO 列表

2. 查找 cls 在當前 MRO 列表中的 index ,並返回它的下一個類,即 mro[index + 1]

當使用 super(cls, inst) 時, Python 會在 inst 的 MRO 列表上搜索 cls 的下一個類. 可以看出, 事實上 super 函數和父類沒有實質性的關聯.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM