Python魔法函數+代碼演示

歡迎訪問的個人博客,博客地址爲 https://haibei.online/

〇、魔術方法定義

  • 在python語法中,往往你會看到有的名稱前面和後面都加上了雙下劃線,由這些名字組成的集合所包含的方法就叫做魔法方法,也叫做特殊方法。
  • python的魔術方法非常強大,然而隨之而來的則是責任。瞭解正確的方法去使用非常重要!

一、總述

1、class類的老大哥 __new__

  • 之所以稱之爲老大哥,是因爲 __new__ 在類執行時第一個被調用,而不是 __init__
  • 如果 __new__ 返回值爲None,那麼 __init__ 根本不會被調用。
  • 舉個例子
class A(object):
    def __new__(cls):
        print("__new__方法被執行")
        return super().__new__(cls)
    def __init__(self):
        print("__init__方法被執行")
 
a = A()

結果如下:

__new__方法被執行
__init__方法被執行

2、實例銷燬: __del__

__del__(self) :當一個實例被銷燬的時候調用的方法

3、類語法糖: __call__

__call__(self[, args...]) 允許一個類的實例像函數一樣被調用: x(a, b) 調用 x.__call__(a, b)

3.1、演示代碼

class A(object):
    def __init__(self, name):
        self.name = name
        print(f"{name}被創造出來了了")

    def __call__(self, *args, **kwargs):
        """允許一個類的實例像函數一樣被調用"""
        print(f"{self.name}打了個響指,一共{len(args)}名超級英雄消失,他們分別是{'、'.join(args)}等。")

    def __del__(self):
        """程序結束(銷燬)後,會自動執行"""
        print(f"{self.name}被銷燬了")

if __name__ == '__main__':
    a = A("滅霸")
    a("蜘蛛俠", "幻世", "黑豹", "奇異博士")

結果如下:

滅霸被創造出來了了
滅霸打了個響指,一共4名超級英雄消失,他們分別是蜘蛛俠、幻世、黑豹、奇異博士等。
滅霸被銷燬了

二、調用系統方法

語法 說明 符號
__len__(self) 定義當被 len 調用時的行爲 len()
__repr__(self) 定義當被 repr 調用時的行爲 repr()
__str__(self) 定義當被 str 調用時的行爲 str()
__bytes__(self) 定義當被 bytes 調用時的行爲 bytes()
__hash__(self) 定義當被 hash 調用時的行爲 hash()
__bool__(self) 定義當被 bool 調用時的行爲 bool()
__format__(self, format_spec) 定義當被 format 調用時的行爲 format()

1、 __repr____str__ 的區別

  1. 當一個類中沒有使用 __repr____str__ 這兩個魔法函數, print() 直接打印實例會返回類名和內存地址

  2. __repr____str__ 這兩個方法都是用於顯示的, __str__ 是面向用戶的,而 __repr__ 面向程序員。

  3. 打印操作會首先嚐試 __str__ ,它通常應該返回一個友好的顯示。

  4. 在交互環境下, __repr__ 會生效,而 __str__ 不會起作用。直接執行實例變量名即可打印,無需 print()

2、參考代碼

class A(object):
    """對照組:沒有使用__str__,打印結果爲類型名及其內存地址"""

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

class B(object):
    """標準組:使用__str__了,打印結果爲__str__魔法函數的返回值"""

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

    def __str__(self):
        return self.info + f"我是{__class__.__name__}類,我用了__str__的魔法函數,真香!"

if __name__ == '__main__':
    demo_message = "真氪金定律:程序員的能力與髮量成反比!快拔頭髮吧!"
    a = A(demo_message)
    b = B(demo_message)
    print(a)
    print(b)

顯示結果如下:

<__main__.A object at 0x1097bf438>
真氪金定律:程序員的能力與髮量成反比!快拔頭髮吧!我是B類,我用了__str__的魔法函數,真香!

2.1、什麼是交互環境

交互環境的典型特點是寫完一行,執行一行。非交互環境要一次執行所有程序

  1. 直接在Terminal中啓動python,就是交互環境
  2. Jupyter Notebook和Lab都是交互環境
  3. Pycharm中的python Console是交互環境
  4. Anaconda Spyder中提供的Console也是交互環境

2.2、在交互環境中 __repr____str__

代碼如下:

[外鏈圖片轉存中…(img-2LbfSCdz-1572167290870)]

  • 可以看出,5行中直接執行 a 因爲定義了 __repr__ ,在交互環境下是可以正確顯示的;
  • 而在7行中,直接執行 b 是無法正確顯示的。

三、有關屬性

語法 說明
__getattr__(self, name) 定義當用戶試圖獲取一個不存在的屬性時的行爲
__getattribute__(self, name) 定義當該類的屬性被訪問時的行爲
__setattr__(self, name, value) 定義當一個屬性被設置時的行爲
__delattr__(self, name) 定義當一個屬性被刪除時的行爲
__dir__(self) 定義當 dir() 被調用時的行爲
__get__(self, instance, owner) 定義當描述符的值被取得時的行爲
__set__(self, instance, value) 定義當描述符的值被改變時的行爲
__delete__(self, instance) 定義當描述符的值被刪除時的行爲

1、 __getattr____setattr__ 存在的問題

  • __getattr____setattr__ 這兩個方法 __init__ 中變量賦值事,就會默認被創建。
  • 舉個例子:傳統賦值方式是 self.a="a"__getattr____setattr__ 就會默認創建出來。
  • 所以當在 __getattr__ 中使用 getattr() 或者 self.xxx , 程序會陷入無限遞歸。
  • 防止遞歸解決方式有兩種,但原理相同。使用超類賦值或者通過超類進行訪問
    1. 超類賦值(新式類):super().setattr(“xxx”, xxx)
    2. 超類訪問(新式類):super().getattr(“xxx”)

2、演示代碼

代碼功能:僞造一個類,使其具有與 dict 類型變量相同的功能;
同時,該類還具備通過’點’來訪問dict的方法

class A(object):
    """僞造字典"""

    def __init__(self, **kwargs):
        super().__setattr__("dict_like", kwargs)  # 超類賦值,防止遞歸

    def __getattr__(self, name):
        """可以調用dict類的系統方法"""
        print("__getattr__被執行:", end="")
        if name in self.dict_like:
            print(f"\t訪問{name}變量", end="")
            return self.dict_like[name]
        else:
            print(f"\t訪問{name}方法", end="")
            return getattr(self.dict_like, name)

    def __setattr__(self, key, value):
        """可以通過'點'的方式,設置字典內容"""
        print("__setattr__被執行:", end="")
        self.dict_like[key] = value

    def __delattr__(self, item):
        print("__delattr__被執行:", end="")
        del self.dict_like[item]

    def __len__(self):
        print("__len__被執行:", end="")
        return len(self.dict_like)

    def __str__(self):
        print("__str__被執行:", end="")
        return str(self.dict_like)

if __name__ == '__main__':
    a = A(a=1, b=2, c=3, d=4, e=5, f=6, g=7)  # 實例化
    print(a)  # 打印
    print(len(a))  # 獲取長度
    a.h = 8  # 字典賦值
    print(a)  # 打印賦值結果
    print("\t|\t訪問b元素:", a.b)  # 訪問b元素
    print(list(a.items()))  # 調用dict的系統方法
    del a.f  # 刪除字典元素
    print(a)  # 打印刪除後的結果

結果如下:

__str__被執行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
__len__被執行:7
__setattr__被執行:__str__被執行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8}
__getattr__被執行:訪問b變量 | 訪問b元素: 2
__getattr__被執行:訪問items方法[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8)]
__delattr__被執行:__str__被執行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'g': 7, 'h': 8}

四、比較操作符

語法 說明 符號
__lt__(self, other) 定義小於號的行爲 x < y
__le__(self, other) 定義小於等於號的行爲 x <= y
__eq__(self, other) 定義等於號的行爲 x == y
__ne__(self, other) 定義不等號的行爲 x != y
__gt__(self, other) 定義大於號的行爲 x > y
__ge__(self, other) 定義大於等於號的行爲 x >= y

五、算數運算符

可以讓類使用算數預算符進行操作

1、標準運算符

說明 代碼 符號 反運算 增強運算
加法 __add__(self, other) + +=
減法 __sub__(self, other) - -=
乘法 __mul__(self, other) * *=
除法 __truediv__(self, other) / /=
整除 __floordiv__(self, other) // //=
模運算 __mod__(self, other) % %=
divmod操作 __divmod__(self, other) divmod()
次方 __pow__(self, other[, modulo]) power()或 ** **=
左位移 __lshift__(self, other) << <<=
右位移 __rshift__(self, other) >> >>=
按位與 __and__(self, other) & &=
按位異或 __xor__(self, other) ^ ^=
按位或 __or__(self, other) | |=

2、反運算

  • 在標準運算符的前面加上“r”就是反運算
  • 假設 ab 是兩個實例,就a而言, a+b 爲標準預算,因爲 a 在前邊; b+a 就是反運算,因爲 a 在後面。
  • 如果 a 想使用反運算,就需要定義 __radd__

3、增量賦值運算

  • 在標準運算符的前面加上“i”就是增量賦值運算

4、代碼實例

4.1、3種運算符的區別

class A(object):
    def __add__(self, other):
        print("A:加法的標準運算")

    def __radd__(self, other):
        print("A:加法的反運算")
        
    def __iadd__(self, other):
        print("A:加法的增強賦值運算")
class B:
    pass

a=A()
b=B()
a+b
b+a
a+=b

輸出結果:

A:加法的標準運算
A:加法的反運算
A:加法的增強賦值運算

4.2、僞造list類,使其具備運算符功能

class A(object):
    """僞造list,通過運算符號,完成增減數據"""

    def __init__(self, *args):
        self.list_like = list(args)  # args默認爲元組類型,需要轉換成爲list

    def __add__(self, other):
        print("__add__被執行:", end="")
        self.list_like.append(other)
        return self

    def __radd__(self, other):
        print("__radd__被執行:", end="")
        self.list_like = [other] + self.list_like
        return self

    def __iadd__(self, other):
        print("__iadd__被執行:", end="")
        self.list_like.append(other)
        return self

    def __mul__(self, other):
        print("__mul__被執行:", end="")
        self.list_like = list(map(lambda x: x * other, self.list_like))
        return self

    def __len__(self):
        return len(self.list_like)

    def __str__(self):
        return str(self.list_like)

if __name__ == '__main__':
    a = A(1, 2, 3, 5, 6, 7)
    a = a + 10  # 向list中最後一位添加數據10
    print(a)
    a = 0 + a  # 向list中第一位添加數據0
    print(a)
    a += 100  # 向list中最後一位添加數據100
    print(a)
    a = a * 6  # 向list中每一位乘以6
    print(a)

輸出結果:

__add__被執行:[1, 2, 3, 5, 6, 7, 10]
__radd__被執行:[0, 1, 2, 3, 5, 6, 7, 10]
__iadd__被執行:[0, 1, 2, 3, 5, 6, 7, 10, 100]
__mul__被執行:[0, 6, 12, 18, 30, 36, 42, 60, 600]

六、一元操作符

語法 說明 符號
__neg__(self) 定義正號的行爲 -x
__pos__(self) 定義負號的行爲 +x
__abs__(self) 定義絕對值的行爲 abs()
__invert__(self) 定義按位求反的行爲 ~x

1、示例代碼

class A(object):
    """僞造list,具備算法運算符功能"""

    def __init__(self, *args):
        self.list_like = list(args)  # args默認爲元組類型,需要轉換成爲list

    def __neg__(self):
        print("__neg__被執行:", end="")
        self.list_like = list(map(lambda x: -x, self.list_like))
        return self

    def __abs__(self):
        print("__abs__被執行:", end="")
        self.list_like = list(map(lambda x: abs(x), self.list_like))
        return self

    def __invert__(self):
        print("__invert__被執行:", end="")
        self.list_like = list(map(lambda x: ~x, self.list_like))
        return self

    def __len__(self):
        return len(self.list_like)

    def __str__(self):
        return str(self.list_like)

if __name__ == '__main__':
    a = A(5, 3, 16, 14, 60, 3)
    a = -a  # 對list中每一位數據取負值
    print(a)
    a = abs(a)  # 對list中每一位數據絕對值
    print(a)
    a = ~a  # 對list中每一位數據取反
    print(a)

輸出結果

__neg__被執行:[-5, -3, -16, -14, -60, -3]
__abs__被執行:[5, 3, 16, 14, 60, 3]
__invert__被執行:[-6, -4, -17, -15, -61, -4]

七、類型轉換

語法 說明
__complex__(self) 定義當被 complex() 調用時的行爲(需要返回恰當的值)
__int__(self) 定義當被 int() 調用時的行爲(需要返回恰當的值)
__float__(self) 定義當被 float() 調用時的行爲(需要返回恰當的值)
__round__(self[, n]) 定義當被 round() 調用時的行爲(需要返回恰當的值)
__index__(self) 當對象是被應用在切片表達式中時,實現整形強制轉換;如果你定義了一個可能在切片時用到的定製的數值型, 你應該定義 index。如果 index 被定義,則 int 也需要被定義,且返回相同的值

八、上下文管理(with 語句)

演示代碼如下:

with open("./1.txt","r",encoding="utf-8") as f:
    data=f.readlines()
語法 說明
__enter__ 1. 定義當使用 with 語句時的初始化行爲;
2. __enter__ 的返回值被 with 語句的目標或者 as 後的名字綁定
__exit__ 1. 定義當一個代碼塊被執行或者終止後上下文管理器應該做什麼
2. 一般被用來處理異常,清除工作或者做一些代碼塊執行完畢之後的日常工作

九、容器類型

語法 說明
__len__(self) 定義當被 len() 調用時的行爲(返回容器中元素的個數)
__getitem__(self, key) 定義獲取容器中指定元素的行爲,相當於 self[key]
__setitem__(self, key, value) 定義設置容器中指定元素的行爲,相當於 self[key] = value
__delitem__(self, key) 定義刪除容器中指定元素的行爲,相當於 del self[key]
__iter__(self) 定義當迭代容器中的元素的行爲
__reversed__(self) 定義當被 reversed() 調用時的行爲
__contains__(self, item) 定義當使用成員測試運算符( innot in )時的行爲

1、 示例代碼

class Avenger(object):
    """復仇者聯盟"""

    def __init__(self, **kwargs):
        self.members = kwargs
        self.names = list(kwargs.keys())
        self.index = 0

    def __getitem__(self, item):
        fighting_num = self.members[item]
        print("__getitem__被執行:", f"正在讀取復仇者【{item}】的信息,其戰鬥力爲{fighting_num}")
        return fighting_num

    def __setitem__(self, key, value):
        print("__setitem__被執行:", f"新增復仇者【{key}】,其戰鬥力爲{value}")
        self.members["key"] = value

    def __delitem__(self, key):
        print("__delitem__被執行:", f"復仇者【{key}】已陣亡!")
        del self.members["key"]

    def __iter__(self):
        print("__iter__被執行")
        return self

    def __next__(self):
        if self.index == len(self.names) - 1:
            raise StopIteration
        else:
            self.index += 1
        return self.names[self.index]

    def __gt__(self, other):
        print("__gt__被執行", f"戰鬥力大於60的復仇者:{other}")
        members = filter(lambda x: x[-1] > other, self.members.items())
        return list(members)

    def __len__(self):
        return len(self.members)

    def __str__(self):
        power_info = map(lambda x: "{0}的戰鬥力爲 {1}".format(*x), self.members.items())
        return "\n".join(power_info)

if __name__ == '__main__':
    a = Avenger(鋼鐵俠=106, 雷神=110, 浩克=120, 奇異博士=90, 蜘蛛俠=80, 美國隊長=50, 幻世=60)
    print(len(a))
    print(f"======== 雷神的戰鬥力:{a['雷神']} ========")
    a["古魯姆"] = 15.6
    del a["幻世"]
    for i in a:
        print(i)
    print(a > 60)

結果如下:

7
__getitem__被執行: 正在讀取復仇者【雷神】的信息,其戰鬥力爲110
======== 雷神的戰鬥力:110 ========
__setitem__被執行: 新增復仇者【古魯姆】,其戰鬥力爲15.6
__delitem__被執行: 復仇者【幻世】已陣亡!
__iter__被執行
雷神
浩克
奇異博士
蜘蛛俠
美國隊長
幻世
__gt__被執行 戰鬥力大於60的復仇者:60
[('鋼鐵俠', 106), ('雷神', 110), ('浩克', 120), ('奇異博士', 90), ('蜘蛛俠', 80)]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章