Python進階——如何正確使用魔法方法?(上)

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"閱讀本文大約需要 10 分鐘。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在做 Python 開發時,我們經常會遇到以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"雙下劃線開頭和結尾","attrs":{}},{"type":"text","text":"的方法,例如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"setitem","attrs":{}}],"attrs":{}},{"type":"text","text":" 等等,這些方法我們通常稱之爲「魔法方法」,而使用這些「魔法方法」,我們可以非常方便地給類添加特殊的功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章,我們就來分析一下,Python 中的魔法方法都有哪些?使用這些魔法方法,我們可以實現哪些實用的功能?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"魔法方法概覽","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們先對 Python 中的魔法方法進行歸類,常見的魔法方法大致可分爲以下幾類:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構造與初始化","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類的表示","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問控制","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比較操作","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器類操作","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可調用對象","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"序列化","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於魔法方法分類較多,這篇文章我們先來看前幾個:構造與初始化、類的表示、訪問控制。剩下的魔法方法,我們會在下一篇文章進行分析講解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"構造與初始化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們來看關於構造與初始化相關的魔法方法,主要包括以下幾種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"init","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於構造與初始化的魔法方法,我們使用最頻繁的一個就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":" 了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在定義類的時候,通常都會去定義構造方法,它的作用就是在初始化一個對象時,定義這個對象的初始值。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __init__(self, name, age):\n self.name = name\n self.age = age\n\np1 = Person('張三', 25)\np2 = Person('李四', 30)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"new","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在初始化一個類的屬性時,除了使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":" 之外,還可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 這個方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在平時開發中使用的雖然不多,但是經常能夠在開源框架中看到它的身影。實際上,這纔是「真正的構造方法」。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __new__(cls, *args, **kwargs):\n print \"call __new__\"\n return object.__new__(cls, *args, **kwargs)\n\n def __init__(self, name, age):\n print \"call __init__\"\n self.name = name\n self.age = age\n\np = Person(\"張三\", 20)\n\n# Output:\n# call __new__\n# call __init__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從例子我們可以看到,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 會在對象實例化時第一個被調用,然後纔會調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":",它們的區別如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 的第一個參數是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cls","attrs":{}}],"attrs":{}},{"type":"text","text":",而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":" 的第一個參數是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"self","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 返回值是一個實例對象,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":" 沒有任何返回值,只做初始化操作","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 由於返回的是一個實例對象,所以它可以給所有實例進行","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"統一","attrs":{}},{"type":"text","text":"的初始化操作","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解了它們之間的區別,我們來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在什麼場景下使用?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 優先於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":" 調用,而且它返回的是一個實例,所以我們可以利用這個特性,在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"new","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法中,每次返回同一個實例來實現一個單例類:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Singleton(object):\n \"\"\"單例\"\"\"\n _instance = None\n def __new__(cls, *args, **kwargs):\n if not cls._instance:\n cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)\n return cls._instance\n\nclass MySingleton(Singleton):\n pass\n\na = MySingleton()\nb = MySingleton()\n\nassert a is b\t# True","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外一個使用場景是,當我們需要","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"繼承內置類","attrs":{}},{"type":"text","text":"時,例如想要繼承 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"int","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"str","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tuple","attrs":{}}],"attrs":{}},{"type":"text","text":",就無法使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"init","attrs":{}}],"attrs":{}},{"type":"text","text":" 來初始化了,只能通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"new","attrs":{}}],"attrs":{}},{"type":"text","text":" 來初始化數據:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass g(float):\n \"\"\"千克轉克\"\"\"\n def __new__(cls, kg):\n return float.__new__(cls, kg * 2)\n\na = g(50) # 50千克轉爲克\nprint a \t# 100\nprint a + 100\t# 200 由於繼承了float,所以可以直接運算,非常方便!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個例子中,我們實現了一個類,這個類繼承了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"float","attrs":{}}],"attrs":{}},{"type":"text","text":",之後,我們就可以對這個類的實例進行計算了,是不是很神奇?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除此之外,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 比較多的應用場景是配合「元類」使用,關於「元類」的原理,我會在後面的文章中講到。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"del","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 這個方法就是我們經常說的「析構方法」,也就是在對象被垃圾回收時被調用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是請注意,當我們執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"del obj","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,這個方法不一定會執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於 Python 是通過引用計數來進行垃圾回收的,如果這個實例在執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"del","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,還被其他對象引用,那麼就不會觸發執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看一個例子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"class Person(object):\n def __del__(self):\n print '__del__'","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們定義了一個帶有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的類,此時我們直接執行:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"a = Person()\nprint 'exit'\n\n# Output:\n# exit\n# __del__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於我們沒有對實例進行任何引用操作時,所以 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在程序退出時被調用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們顯示執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"del obj","attrs":{}}],"attrs":{}},{"type":"text","text":",如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"a = Person()\ndel a \t# 手動銷燬對象\nprint 'exit'\n\n# Output:\n# __del__\n# exit","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣地,由於實例沒有被其他對象所引用,當我們手動銷燬這個實例時,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 被調用後程序正常退出。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果這個對象被其他對象所引用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"a = Person()\nb = a # b引用a\ndel a # 手動銷燬 不觸發__del__\nprint 'exit'\n\n# Output:\n# exit\n# __del__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,如果這個實例有被其他對象引用,儘管我們手動銷燬這個實例,但不會觸發 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,而是在程序正常退出時被調用執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常來說,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__del__","attrs":{}}],"attrs":{}},{"type":"text","text":" 這個方法我們很少會使用到,除非需要在顯示執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"del","attrs":{}}],"attrs":{}},{"type":"text","text":" 執行特殊清理邏輯的場景中才會使用到。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但另一方面,也給我們一個提醒,當我們在對文件、Socket 進行操作時,如果要想安全地關閉和銷燬這些對象,最好是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"try","attrs":{}}],"attrs":{}},{"type":"text","text":" 異常塊後的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"finally","attrs":{}}],"attrs":{}},{"type":"text","text":" 中進行關閉和釋放操作,從而避免資源的泄露。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"類的表示","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們來看關於類的表示相關的魔法方法,主要包括以下幾種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" / ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__unicode__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__hash__","attrs":{}}],"attrs":{}},{"type":"text","text":" / ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__eq__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__nozero__","attrs":{}}],"attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"str","attrs":{}},{"type":"text","text":"__/__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"repr","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 這 2 個魔法方法,非常類似,很多人區分不出它們有什麼不同,我們來看幾個例子,就能理解這 2 個方法的效果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":">>> a = 'hello'\n>>> str(a)\n'hello'\n>>> '%s' % a\t# 調用__str__\n'hello'\n\n>>> repr(a)\t\t# 對象a的標準表示 也就是a是如何創建的\n\"'hello'\"\n>>> '%r' % a\t# 調用__repr__\n\"'hello'\"\n\n>>> import datetime\n>>> b = datetime.datetime.now()\n>>> str(b)\n'2017-02-22 12:28:40.923379'\n>>> print b\t\t# 等同於print str(b)\n2017-02-22 12:28:40.923379\n\n>>> repr(b)\t\t# 展示對象b的標準創建方式(如何創建的)\n'datetime.datetime(2017, 2, 22, 12, 28, 40, 923379)'\n>>> b\t\t # 等同於print repr(b)\ndatetime.datetime(2017, 2, 22, 12, 28, 40, 923379)\n\n>>> c = eval(repr(b))\t# repr(b)目標針對於機器 所以可執行\n>>> c\ndatetime.datetime(2017, 2, 22, 12, 28, 40, 923379)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從上述例子中我們可以看出這 2 個方法的區別:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 強調可讀性,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 強調準確性 / 標準性","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 的目標人羣是用戶,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 的目標人羣是機器,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 返回的結果是可執行的,通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eval(repr(obj))","attrs":{}}],"attrs":{}},{"type":"text","text":" 可以正確運行","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"佔位符 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"%s","attrs":{}}],"attrs":{}},{"type":"text","text":" 調用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":",而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"%r","attrs":{}}],"attrs":{}},{"type":"text","text":" 調用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"repr","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我們在實際中開發中定義類時,一般這樣使用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __init__(self, name, age):\n self.name = name\n self.age = age\n\n def __str__(self):\n # 格式化 友好對用戶展示\n return 'name: %s, age: %s' % (self.name, self.age)\n\n def __repr__(self):\n # 標準化展示\n return \"Person('%s', %s)\" % (self.name, self.age)\n\nperson = Person('zhangsan', 20)\n\n# 強調對用戶友好\nprint str(person) # name: zhangsan, age: 20 \nprint '%s' % person # name: zhangsan, age: 20\n\n# 強調對機器友好 結果 eval 可執行\nprint repr(person)\t # Person('zhangsan', 20)\nprint '%r' % person # Person('zhangsan', 20)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明白了它們之間的區別,我們再思考一下,如果只定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 或 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 其中一個,那會是什麼結果?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":",但沒有定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __init__(self, name, age):\n self.name = name\n self.age = age\n \n def __str__(self):\n return 'name: %s, age: %s' % (self.name, self.age)\n\nperson = Person('zhangsan', 20)\n\nprint str(person) # name: zhangsan, age: 20 \nprint '%s' % person # name: zhangsan, age: 20\n\nprint repr(person)\t # <__main__.person object="" at="">\nprint '%r' % person # <__main__.person object="" at="">","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":",但沒有定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __init__(self, name, age):\n self.name = name\n self.age = age\n \n def __repr__(self):\n return \"Person('%s', %s)\" % (self.name, self.age)\n\nperson = Person('zhangsan', 20)\n\nprint str(person) # Person('zhangsan', 20)\nprint '%s' % person # Person('zhangsan', 20)\n\nprint repr(person)\t # Person('zhangsan', 20)\nprint '%r' % person # Person('zhangsan', 20)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從例子中我們可以看到結果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果只定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_str__","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"repr(person)","attrs":{}}],"attrs":{}},{"type":"text","text":" 輸出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"<__main__.person object="" at="">","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果只定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"str(person)","attrs":{}}],"attrs":{}},{"type":"text","text":" 與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"repr(person)","attrs":{}}],"attrs":{}},{"type":"text","text":" 結果是相同的","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在表示類時,是一級的,如果只定義它,那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__ = __repr__","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 展示類時是次級的,如果沒有定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"repr(person)","attrs":{}}],"attrs":{}},{"type":"text","text":" 將會展示缺省的定義。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"unicode","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個類定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__unicode__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,那麼在調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"unicode(obj)","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,此方法將被調用,但是其返回值類型是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"unicode","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __unicode__(self):\n # 這裏不是u'hello'\n return 'hello'\n \nperson = Person()\nprint unicode(person)\t # helllo\nprint type(unicode(person))\t # ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從例子中我們可以看到, 雖然我們定義的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__unicode__","attrs":{}}],"attrs":{}},{"type":"text","text":" 返回值不是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"unicode","attrs":{}}],"attrs":{}},{"type":"text","text":" 類型,但在輸出時,程序會自動轉換成 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"unicode","attrs":{}}],"attrs":{}},{"type":"text","text":" 類型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個方法在開發中一般很少使用,通常我們只需要定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"hash","attrs":{}},{"type":"text","text":"__/__eq__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__hash__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法返回一個整數,用來表示實例對象的唯一標識,配合 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__eq__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,可以判斷兩個對象是否相等:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n def __init__(self, uid):\n self.uid = uid\n \n\tdef __repr__(self):\n return 'Person(%s)' % self.uid\n \n def __hash__(self):\n return self.uid\n \n def __eq__(self, other):\n return self.uid == other.uid\n \np1 = Person(1)\np2 = Person(1)\np1 == p2\t # True\n\np3 = Person(2)\nprint set([p1, p2, p3])\t# 根據唯一標識去重輸出 set([Person(1), Person(2)])","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們需要判斷兩個對象是否相等,只需要我們重寫 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__hash__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__eq__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法就可以了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,當我們使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"set","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"set","attrs":{}}],"attrs":{}},{"type":"text","text":" 中存放這些對象,也會根據這兩個方法進行去重操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"nonzero","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"bool(obj)","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,會調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__nonzero__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,返回 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"True","attrs":{}}],"attrs":{}},{"type":"text","text":" 或 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"False","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n def __init__(self, uid):\n self.uid = uid\n\n def __nonzero__(self):\n return self.uid > 10\n \np1 = Person(1)\np2 = Person(15)\nprint bool(p1)\t # False\nprint bool(p2)\t # True","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Python3 中,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__nonzero__","attrs":{}}],"attrs":{}},{"type":"text","text":" 被重命名爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__bool__","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"訪問控制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們來看關於訪問控制的魔法方法,主要包括以下幾種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__setattr__","attrs":{}}],"attrs":{}},{"type":"text","text":":通過「.」設置屬性或 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"setattr(key, value)","attrs":{}}],"attrs":{}},{"type":"text","text":" 設置屬性時調用","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":":訪問不存在的屬性時調用","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__delattr__","attrs":{}}],"attrs":{}},{"type":"text","text":":刪除某個屬性時調用","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__getattribute__","attrs":{}}],"attrs":{}},{"type":"text","text":":訪問任意屬性或方法時調用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看使用這些方法的完整例子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# coding: utf8\n\nclass Person(object):\n\n def __setattr__(self, key, value):\n \"\"\"屬性賦值\"\"\"\n if key not in ('name', 'age'):\n return\n if key == 'age' and value < 0:\n raise ValueError()\n super(Person, self).__setattr__(key, value)\n\n def __getattr__(self, key):\n \"\"\"訪問某個不存在的屬性\"\"\"\n return 'unknown'\n\n def __delattr__(self, key):\n \"\"\"刪除某個屬性\"\"\"\n if key == 'name':\n raise AttributeError()\n super(Person, self).__delattr__(key)\n\n def __getattribute__(self, key):\n \"\"\"所有屬性/方法調用都經過這裏\"\"\"\n if key == 'money':\n return 100\n if key == 'hello':\n return self.say\n return super(Person, self).__getattribute__(key)\n\n def say(self):\n return 'hello'\n \np1 = Person()\np1.name = 'zhangsan'\t# 調用__setattr__\np1.age = 20 # 調用__setattr__\nprint p1.name\t # zhangsan\nprint p1.age\t # 20\n\nsetattr(p1, 'name', 'lisi')\t# 調用__setattr__\nsetattr(p1, 'age', 30)\t\t # 調用__setattr__\nprint p1.name\t # lisi\nprint p1.age\t # 30\n\np1.gender = 'male'\t # __setattr__中忽略對gender賦值\nprint p1.gender\t # gender不存在 所以會調用__getattr__返回unknown\n\nprint p1.money\t # money不存在 在__getattribute__中返回100\n\nprint p1.say()\t # hello\nprint p1.hello() # hello 調用__getattribute__ 間接調用say方法\n\ndel p1.name\t\t # __delattr__中引發AttributeError\n\np2 = Person()\np2.age = -1\t\t # __setattr__中引發ValueError","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們仔細看一下這個例子,我已經添加好了詳細的註釋。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"setattr","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先來說 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__setattr__","attrs":{}}],"attrs":{}},{"type":"text","text":",當我們在給一個對象進行屬性賦值時,都會經過這個方法,在這個例子中,我們只允許對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"name","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"age","attrs":{}}],"attrs":{}},{"type":"text","text":" 這 2 個屬性進行賦值,忽略了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"gender","attrs":{}}],"attrs":{}},{"type":"text","text":" 屬性,除此之外,我們還對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"age","attrs":{}}],"attrs":{}},{"type":"text","text":" 賦值進行了校驗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__setattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,我們可以非常方便地對屬性賦值進行控制。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"getattr","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":",由於我們在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__setattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 中忽略了對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"gender","attrs":{}}],"attrs":{}},{"type":"text","text":" 屬性的賦值,所以當訪問這個不存在的屬性時,會調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"getattr","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,在這個方法中返回了默認值 unknown。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多同學以爲這個方法與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__setattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法對等的,一個是賦值,一個是獲取。其實不然,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 只有在訪問「不存在的屬性」時纔會被調用,這裏我們需要注意。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"getattribute","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 後,還有一個和它非常類似的方法:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattribute__","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多人經常把這個方法和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 混淆,通過例子我們可以看出,它與前者的區別在於:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 只有在訪問不存在的屬性時被調用,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattribute__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在訪問任意屬性時都會被調用","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__getattr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 只針對屬性訪問,而","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__getattribute__","attrs":{}}],"attrs":{}},{"type":"text","text":" 不僅針對所有屬性訪問,還包括方法調用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的例子,雖然我們沒有定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"money","attrs":{}}],"attrs":{}},{"type":"text","text":" 屬性和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"hello","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,但是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"getattribute","attrs":{}},{"type":"text","text":"_","attrs":{}}],"attrs":{}},{"type":"text","text":" 裏攔截到了這個屬性和方法,就可以對其執行不同的邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"__","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"delattr","attrs":{}},{"type":"text","text":"__","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,我們來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__delattr__","attrs":{}}],"attrs":{}},{"type":"text","text":",它比較簡單,當刪除對象的某個屬性時,這個方法會被調用,所以它一般會用在刪除屬性前的校驗場景中使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章,我們主要介紹了 Python 中常見的魔法方法,主要有構造與初始化、類的表示、訪問控制這 3 個模塊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構造與初始化的魔法方法,常常用在類的初始化過程中,其中 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__init__","attrs":{}}],"attrs":{}},{"type":"text","text":"一般用於實例初始化, 而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__new__","attrs":{}}],"attrs":{}},{"type":"text","text":" 可以改變初始化實例的行爲,通過它我們可以實現一個單例或者繼承一個內置類。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於類的表示的魔法方法,比較常用的,當我們想表示一個類時,可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__str__","attrs":{}}],"attrs":{}},{"type":"text","text":" 或 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__repr__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,當需要判斷兩個對象是否相等時,可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__hash__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"eq","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於訪問控制的魔法方法,它可以控制實例的屬性賦值、屬性訪問、方法訪問、屬性刪除等操作,這對於我們實現一個複雜功能的類有很大幫助。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下一篇文章,我們會繼續分析剩下的魔法方法,主要包括關於比較操作、容器類操作、可調用對象、序列化相關的魔法方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c10b027be63b0c5c1ffc36a76a4a974b.png","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"關注「水滴與銀彈」公衆號,7年資深後端研發,和你分享更多優質技術乾貨。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章