本系列會著重在紀錄學習Python的筆記,如果有任何問題或是錯誤的地方,可以直接留言或私訊我,自學錯誤很難看到問題點,還請各位多多指教。

Python Magic Method 直接翻譯就叫”魔法方法 or 魔術方法”,這個神奇的方法也就是雙底線開頭雙底線結尾,我們常見要定義class屬性時使用的__init__也是Magic Method~,然後我會看Python的document然後一個一個去了解Magic Method到底在做什麼以及怎麼使用。


object.__new__(cls[, …])

  • new

  • Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument.

  • __new__ 會在__init__ 之前執行, 其主要功能是實例__init__所指定的屬性。 如果__new__沒有return cls 則__new__不會被調用。 
    以下是範例:

    # new會在init前執行
    class MagicNew:
        def __new__(cls, *args, **kwargs):
            print("here is new")
            instance = object.__new__(cls)
            return instance
    
        def __init__(self, name):
            print("here is init")
            self.name = name
    
    c = MagicNew("John")
    # output:
    # here is new
    # here is init
  • 以下是使用情境為設計模式之單例模式: 
    參考網址:https://zhuanlan.zhihu.com/p/35943253

# 單例模式,該物件存在就不會在new一個新的出來
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance

s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)
# output:
# <__main__.Singleton object at 0x104808d90>
# <__main__.Singleton object at 0x104808d90>
# 注意這邊的記憶體位置 s1 s2 會是同一個物件

object.__init__(self[, …])

  • initialization
  • Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression.
  • __new__() to create it, and __init__() to customize it
    __init__就是大家常見的定義該物件屬性的方式了。
# 在new出物件時可以透過init來指定該物件所擁有的屬性
class MagicInit:
    def __init__(self, name: str, age: int, say_something: str = "I am default"):
        self.name = name
        self.age = age
        self.say_something = say_something

p1 = MagicInit("John", 25)
p2 = MagicInit("John", 25, "change the default")
print(p1.name, p1.age, p1.say_something)
print(p2.name, p2.age, p2.say_something)
# output:
# John 25 I am default
# John 25 change the default
  • 不過據我所知好像有滿多奇妙的操作可以在 __init__ 完成,畢竟new出一個物件的時候執行完 __new__ 就會執行 __init__,根據不同的使用情境或許能有不同的操作物件。(待學習….)

object.__del__(self)

  • Called when the instance is about to be destroyed.
  • __del__當該物件被消除時會call到這個magic method 
  • 根據官方文件表示:
    • del x doesn’t directly call x.__del__() - the former decrements the reference count for x by one, and the latter is only called when x’s reference count reaches zero.
    • del <物件>時並不會直接去call __del__()而會先去扣他所關聯的計算,而物件的new出來初始reference count值為1,隨著使用(指定變數為該物件時)會+1,而del <物件>時該數會-1,直到該物件的reference count為0時才會去call __del__()。 (下面使用sys.getrefcount(<物件>)也算使用唷)
      參考文章:https://www.796t.com/content/1542840125.html
import time

class MagicDel:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print("刪除了")

p = MagicDel("John")
del p
print(p)
time.sleep(2)

# output:
# 刪除了
# Traceback (most recent call last):
# NameError: name 'p' is not defined
  • 一開始先new出一個物件出來後直接 del <物件>該物件就不會存在程式所以就會觸發__del__()。
# 當今天使用該物件不只一次時
import time
import sys

class MagicDel:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print("刪除了")

# sys.getrefcount() 可以取得reference count
p = MagicDel("John")
print(sys.getrefcount(p))
p1 = p
print(sys.getrefcount(p))
del p1
print(sys.getrefcount(p))
print("等待兩秒鐘")
time.sleep(2)

# output:
# 2
# 3
# 2
# 等待兩秒鐘後
# 刪除了
  • 而當今天讓多個物件去使用這個class則會顯示如上圖,而在整個程式結束後才會觸發 __del__()。
    目前還不清楚什麼情況下會有機會去使用。(待學習….)