刻意練習Python基礎 ——day 15:魔法方法(上)

17天 —— Python基礎刻意練習

Day 01——Task01:變量、運算符與數據類型
Day 02——Task02:條件與循環
Day 03——Task03:列表與元組(上)
Day 04——Task03:列表與元組(下)
Day 05——Task04:字符串與序列
Day 06——Task05:函數與Lambda表達式(上)
Day 07——Task05:函數與Lambda表達式(下)
Day 08——Task06:字典與集合
Day 09——Task07:文件與文件系統(上)
Day 10——Task07:文件與文件系統(下)
Day 11——Task08:異常處理
Day 12——Task09:else 與 with 語句
Day 13——Task10:類與對象(上)
Day 14——Task10:類與對象(下)
Day 15——Task11:魔法方法(上)
Day 16——Task11:魔法方法(下)
Day 17——Task12:模塊
    



魔法方法



1. 基礎語法

  魔法方法總是被雙下劃線包圍,例如__init__

  魔法方法是面向對象的 Python 的一切,如果你不知道魔法方法,說明你還沒能意識到面向對象的 Python 的強大。

  魔法方法的“魔力”體現在它們總能夠在適當的時候被自動調用。

  魔法方法的第一個參數應爲cls(類方法) 或者self(實例方法)。

  • cls:代表一個類的名稱

  • self:代表一個實例對象的名稱






2.魔法方法


 2.1. 基本的魔法方法


  2.1.1 __init__(self[, ...])

  • 構造器,當一個實例被創建的時候調用的初始化方法
class Rectangle:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getPeri(self):
        return (self.x + self.y) * 2

    def getArea(self):
        return self.x * self.y


rect = Rectangle(4, 5)
print(rect.getPeri())  # 18
print(rect.getArea())  # 20



  2.1.2 __new__(cls[, ...])

  • __new__是在一個對象實例化的時候所調用的第一個方法,在調用__init__初始化前,先調用__new__
  • __new__至少要有一個參數cls,代表要實例化的類,此參數在實例化時由 Python 解釋器自動提供,後面的參數直接傳遞給__init__
  • __new__對當前類進行了實例化,並將實例返回,傳給__init__self。但是,執行了__new__,並不一定會進入__init__,只有__new__返回了,當前類cls的實例,當前類的__init__纔會進入。
class A(object):
    def __init__(self, value):
        print("into A __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into A __new__")
        print(cls)
        return object.__new__(cls)


class B(A):
    def __init__(self, value):
        print("into B __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into B __new__")
        print(cls)
        return super().__new__(cls, *args, **kwargs)


b = B(10)

# 結果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.B'>
# into B __init__

class A(object):
    def __init__(self, value):
        print("into A __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into A __new__")
        print(cls)
        return object.__new__(cls)


class B(A):
    def __init__(self, value):
        print("into B __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into B __new__")
        print(cls)
        return super().__new__(A, *args, **kwargs)  # 改動了cls變爲A


b = B(10)

# 結果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
  • __new__沒有正確返回當前類cls的實例,那__init__是不會被調用的,即使是父類的實例也不行,將沒有__init__被調用。
  • 可利用__new__實現單例模式。
class Earth:
    pass


a = Earth()
print(id(a))  # 260728291456
b = Earth()
print(id(b))  # 260728291624

class Earth:
    __instance = None  # 定義一個類屬性做判斷

    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls)
            return cls.__instance
        else:
            return cls.__instance


a = Earth()
print(id(a))  # 512320401648
b = Earth()
print(id(b))  # 512320401648
  • __new__方法主要是當你繼承一些不可變的class時(比如int, str, tuple), 提供給你一個自定義這些類的實例化過程的途徑。
class CapStr(str):
    def __new__(cls, string):
        string = string.upper()
        return str.__new__(cls, string)


a = CapStr("i love lsgogroup")
print(a)  # I LOVE LSGOGROUP



  2.1.3 __del__(self)

  • 析構器,當一個對象將要被系統回收之時調用的方法。

Python 採用自動引用計數(ARC)方式來回收對象所佔用的空間,當程序中有一個變量引用該 Python 對象時,Python 會自動保證該對象引用計數爲 1;當程序中有兩個變量引用該 Python 對象時,Python 會自動保證該對象引用計數爲 2,依此類推,如果一個對象的引用計數變成了 0,則說明程序中不再有變量引用該對象,表明程序不再需要該對象,因此 Python 就會回收該對象。
大部分時候,Python 的 ARC 都能準確、高效地回收系統中的每個對象。但如果系統中出現循環引用的情況,比如對象 a 持有一個實例變量引用對象 b,而對象 b 又持有一個實例變量引用對象 a,此時兩個對象的引用計數都是 1,而實際上程序已經不再有變量引用它們,系統應該回收它們,此時 Python 的垃圾回收器就可能沒那麼快,要等專門的循環垃圾回收器(Cyclic Garbage Collector)來檢測並回收這種引用循環。

class C(object):
    def __init__(self):
        print('into C __init__')

    def __del__(self):
        print('into C __del__')


c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__



  2.1.4 __str__ 和 __repr__

  __str__(self):

  • 當你打印一個對象的時候,觸發__str__
  • 當你使用%s格式化的時候,觸發__str__
  • str強轉數據類型的時候,觸發__str__

  __repr__(self):

  • repr是str的備胎
  • __str__的時候執行__str__,沒有實現__str__的時候,執行__repr__
  • repr(obj)內置函數對應的結果是__repr__的返回值
  • 當你使用%r格式化的時候 觸發__repr__
class Cat:
    """定義一個貓類"""

    def __init__(self, new_name, new_age):
        """在創建完對象之後 會自動調用, 它完成對象的初始化的功能"""
        self.name = new_name
        self.age = new_age

    def __str__(self):
        """返回一個對象的描述信息"""
        return "名字是:%s , 年齡是:%d" % (self.name, self.age)
        
    def __repr__(self):
        """返回一個對象的描述信息"""
        return "Cat:(%s,%d)" % (self.name, self.age)

    def eat(self):
        print("%s在喫魚...." % self.name)

    def drink(self):
        print("%s在喝可樂..." % self.name)

    def introduce(self):
        print("名字是:%s, 年齡是:%d" % (self.name, self.age))


# 創建了一個對象
tom = Cat("湯姆", 30)
print(tom)  # 名字是:湯姆 , 年齡是:30
print(str(tom)) # 名字是:湯姆 , 年齡是:30
print(repr(tom))  # Cat:(湯姆,30)
tom.eat()  # 湯姆在喫魚....
tom.introduce()  # 名字是:湯姆, 年齡是:30

  __str__(self) 的返回結果可讀性強。也就是說,__str__的意義是得到便於人們閱讀的信息,就像下面的 ‘2019-10-11’ 一樣。

  __repr__(self) 的返回結果應更準確。怎麼說,__repr__存在的目的在於調試,便於開發者使用。

import datetime

today = datetime.date.today()
print(str(today))  # 2019-10-11
print(repr(today))  # datetime.date(2019, 10, 11)
print('%s' %today)  # 2019-10-11
print('%r' %today)  # datetime.date(2019, 10, 11)




 2.2. 算術運算符

  • 類型工廠函數,指的是不通過類而是通過函數來創建對象。
class C:
    pass


print(type(len))  # <class 'builtin_function_or_method'>
print(type(dir))  # <class 'builtin_function_or_method'>
print(type(int))  # <class 'type'>
print(type(list))  # <class 'type'>
print(type(tuple))  # <class 'type'>
print(type(C))  # <class 'type'>
print(int('123'))  # 123

# 這個例子中list工廠函數把一個元祖對象加工成了一個列表對象。
print(list((1, 2, 3)))  # [1, 2, 3]



  2.2.1.__add____sub__

  • __add__(self, other)定義加法的行爲:+
  • __sub__(self, other)定義減法的行爲:-
class MyClass:

    def __init__(self, height, weight):
        self.height = height
        self.weight = weight

    # 兩個對象的長相加,寬不變.返回一個新的類
    def __add__(self, others):
        return MyClass(self.height + others.height, self.weight + others.weight)

    # 兩個對象的寬相減,長不變.返回一個新的類
    def __sub__(self, others):
        return MyClass(self.height - others.height, self.weight - others.weight)

    # 說一下自己的參數
    def intro(self):
        print("高爲", self.height, " 重爲", self.weight)


def main():
    a = MyClass(height=10, weight=5)
    a.intro()

    b = MyClass(height=20, weight=10)
    b.intro()

    c = b - a
    c.intro()

    d = a + b
    d.intro()


if __name__ == '__main__':
    main()

# 高爲 10  重爲 5
# 高爲 20  重爲 10
# 高爲 10  重爲 5
# 高爲 30  重爲 15



  2.2.2. 其他的操作符

魔法方法 含義
__mul__(self, other) 定義乘法的行爲:*
__truediv__(self, other) 定義真除法的行爲:/
__floordiv__(self, other) 定義整數除法的行爲://
__mod__(self, other) 定義取模算法的行爲:%
__divmod__(self, other) 定義當被 divmod() 調用時的行爲
divmod(a, b) 把除數和餘數運算結果結合起來,返回一個包含商和餘數的元組(a // b, a % b)。
__pow__(self, other[, module]) 定義當被power() 調用或**運算時的行爲
__lshift__(self, other) 定義按位左移位的行爲:<<
__rshift__(self, other) 定義按位右移位的行爲:>>
__and__(self, other) 定義按位與操作的行爲:&
__xor__(self, other) 定義按位異或操作的行爲:^
__or__(self, other) 定義按位或操作的行爲:|



 2.3. 反算術運算符

  • 反運算魔方方法,與算術運算符保持一一對應,不同之處就是反運算的魔法方法多了一個“r”。當文件左操作不支持相應的操作時被調用。
    |魔法方法| 含義 |
    |–|--|
    |__radd__(self, other)|定義加法的行爲:+
    |__rsub__(self, other)|定義減法的行爲:-
    |__rmul__(self, other)|定義乘法的行爲:*
    |__rtruediv__(self, other)|定義真除法的行爲:/
    |__rfloordiv__(self, other)|定義整數除法的行爲://
    |__rmod__(self, other)|定義取模算法的行爲:%
    |__rdivmod__(self, other)|定義當被 divmod()調用時的行爲
    |__rpow__(self, other[, module])|定義當被power()調用或** 運算時的行爲
    |__rlshift__(self, other)|定義按位左移位的行爲:<<
    |__rrshift__(self, other)|定義按位右移位的行爲:>>
    |__rand__(self, other)|定義按位與操作的行爲:&
    |__rxor__(self, other)|定義按位異或操作的行爲:^
    |__ror__(self, other)|定義按位或操作的行爲:|



  舉個栗子:a + b
  這裏加數是a,被加數是b,因此是a主動,反運算就是如果a對象的__add__()方法沒有實現或者不支持相應的操作,那麼 Python 就會調用b__radd__()方法。

class Nint(int):
    def __radd__(self, other):
        return int.__sub__(other, self) # 注意 self 在後面


a = Nint(5)
b = Nint(3)
print(a + b)  # 8
print(1 + b)  # -2



 2.4. 增量賦值運算符

魔法方法 含義
__iadd__(self, other) 定義賦值加法的行爲:+=
__isub__(self, other) 定義賦值減法的行爲:-=
__imul__(self, other) 定義賦值乘法的行爲:*=
__itruediv__(self, other) 定義賦值真除法的行爲:/=
__ifloordiv__(self, other) 定義賦值整數除法的行爲://=
__imod__(self, other) 定義賦值取模算法的行爲:%=
__ipow__(self, other[, modulo]) 定義賦值冪運算的行爲:**=
__ilshift__(self, other) 定義賦值按位左移位的行爲:<<=
__irshift__(self, other) 定義賦值按位右移位的行爲:>>=
__iand__(self, other) 定義賦值按位與操作的行爲:&=
__ixor__(self, other) 定義賦值按位異或操作的行爲:^=
__ior__(self, other) 定義賦值按位或操作的行爲:|=



 2.5. 一元運算符

魔法方法 含義
__neg__(self) 定義正號的行爲:+x
__pos__(self) 定義負號的行爲:-x
__abs__(self) 定義當被abs()調用時的行爲
__invert__(self) 定義按位求反的行爲:~x






參考資料

  • https://blog.csdn.net/LSGO_MYP/article/details/102887712
  • https://www.runoob.com/python3/python3-tutorial.html
  • https://www.bilibili.com/video/av4050443
  • http://c.biancheng.net/view/2371.html
  • https://www.cnblogs.com/seablog/p/7173107.html
  • https://www.cnblogs.com/Jimmy1988/p/6804095.html
  • https://blog.csdn.net/johnsonguo/article/details/585193
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章