本編研究下python的內置屬性/魔法方法/魔法函數
這裏說的內置屬性,是指
__xxx__
的方法和屬性,有的地方也稱爲魔法方法,叫法不一樣。
本文概要
- 1.
__init__
和__new__
的順序、使用? - 2.
__getattribute__
幹嘛的? - 3.
__call__
作用,與callable
內置函數有着怎樣的關係呢? - 4.對象如何才能比較?
__lt__
、__gt__
等 - 5.
__getattr__
、__setattr__
、__delattr__
何時觸發? - 6.
__add__
與__iadd__
區別,何時觸發(+
、+=
)?什麼時候會改變自身,什麼時候會新建對象? - 7.
__getitem__
、__setitem__
、__delitem__
何時觸發? - 8.迭代
__iter__
、__next__
與for-in
循環的配合 - 9.
__mul__
、__rmul__
、__imul__
何時觸發?什麼時候會改變自身,什麼時候會新建對象?
如果上面幾點問題都能回答上,那麼可以跳過本篇文章了。本篇相關文章共三連彈。(端午節在家整理,有幫助記得點個👍,就是對我最大的肯定😘😘😘)
技巧:文章內容太多如何閱讀?
- 先看文章的目錄(TOC),掌握文章整體脈絡
- 然後走馬觀花式,通篇閱讀。如果文章有用,再細細研究;文章沒用的直接忽略,找下篇。
- 如果爲了解決問題的,解決完了有時間再細讀
- 如果學習的,收藏起來,邊學習,邊敲代碼實踐 (別copy代碼,自己敲)
- 收藏完,下次用的時候,忘記如何使用了,到文章直接CTRL+F查找關鍵字
這提到鏈接收藏功能,推薦下 ㊙鏈接管理工具🔗 裏的【我的鏈接】可以管理自己的鏈接哦 (個人鏈接屬於私人資產,所以要註冊個賬戶才能使用哦👇👇👇)
浩瀚的網絡中,你我的相遇也是種緣分,看你天資聰慧、骨骼精奇,就送你一場大造化吧,能領悟多少就看你自己了。㊙傳承之地🙇
1.常見魔法函數
常用專有屬性 | 說明 | 觸發方式 |
---|---|---|
__init__ |
構造初始化函數 | 創建實例後,賦值時使用,在__new__ 後 |
__new__ |
生成實例所需屬性 | 創建實例時 |
__class__ |
實例所在的類 | 實例.__class__ |
__str__ |
實例字符串表示,可讀性 | print(類實例),如沒實現,使用repr結果 |
__repr__ |
實例字符串表示,準確性 | 類實例 回車 或者 print(repr(類實例)) |
__del__ |
析構 | del刪除實例 |
__dict__ |
實例自定義屬性 | vars(實例.__dict__) |
__doc__ |
類文檔,子類不繼承 | help(類或實例) |
__getattribute__ |
屬性訪問攔截器 | 訪問實例屬性時 |
__delattr__(s,name) |
刪除name屬性 | 調用時 |
__gt__(self,other) |
判斷self對象是否大於other對 | 調用時 |
__setattr__(s,name,value) |
設置name屬性 | 調用時 |
__gt__(self,other) |
判斷self對象是否大於other對象 | 調用時 |
__lt__(slef,other) |
判斷self對象是否小於other對象 | 調用時 |
__ge__(slef,other) |
判斷self對象是否大於或者等於other對象 | 調用時 |
__le__(slef,other) |
判斷self對象是否小於或者等於other對象 | 調用時 |
__eq__(slef,other) |
判斷self對象是否等於other對象 | 調用時 |
__call__(self,\*args) |
把實例對象作爲函數調用 | 調用時 |
下面分別以object和list的內置屬性(魔法方法)來講解下,其他的有需要的可以留言補充。
2.object
使用
dir()
函數來可以打印出有哪些屬性,下面會挑一些常用的演示
演示
class People:
name: str
class MyTestCase(unittest.TestCase):
def test_attr(self):
print(dir(People))
如需要源碼,可以查看。(關鍵代碼都貼在文章裏,不用看也行)
2-1.init
初始化時,調用__init__
方法
演示
class People:
name: str = 'iworkh'
def __init__(self, name):
print('call...__init__')
self.name = name
class MyTestCase(unittest.TestCase):
def test(self):
people = People('沐雨雲樓') # 創建對象時,調用__init__
print(people.name)
2-2.new
__ new__ ()
在__ init__()
之前被調用,用於生成實例對象.
利用這個方法和類屬性的特性可以實現設計模式中的單例模式.單例模式是指創建唯一對象嗎,單例模式設計的類只能實例化一個對象.
演示
class People:
name: str = 'iworkh'
def __new__(cls, *args, **kwargs):
print('call...__new__')
obj = object.__new__(cls)
return obj
def __init__(self, name):
print('call...__init__')
self.name = name
class MyTestCase(unittest.TestCase):
def test(self):
people = People('沐雨雲樓') # 創建對象先調用__new__,初始化時調用__init__
print(people.name)
2-3.str
__ str__ ()
用於表示對象代表的含義,返回一個字符串.實現了__ str__ ()
方法.
- 可以直接使用print語句輸出對象,
- 可以通過函數str()觸發
__ str__ ()
的執行.這樣就把對象和字符串關聯起來,便於某些程序的實現,可以用這個字符串來表示某個類
演示
class People:
name: str = 'iworkh'
age: int = 20
def __str__(self) -> str:
print("call...__str__")
return "{{'name': {}, 'age': {}}}".format(self.name, self.age)
class MyTestCase(unittest.TestCase):
def test(self):
people = People()
print(people) # {'name': iworkh, 'age': 20}
print(str(people)) # {'name': iworkh, 'age': 20}
2-4.doc
類文檔,子類不會繼承
help(類或實例)會觸發
演示
class People:
"""
人
"""
name: str = 'iworkh'
age: int = 20
def say(self):
"""
say method
"""
print('say')
class MyTestCase(unittest.TestCase):
def test(self):
people = People()
print(People.__doc__) # 類: 人
print(people.__doc__) # 對象:人
print(People.say.__doc__) # 類:say method
print(people.say.__doc__) # 對象:say method
print(help(People.say)) # 類:say method
2-5.dict
實例自定義屬性,只有_init__
和通過對象.xxx
賦值的屬性,纔會顯示
vars(實例)
會觸發
演示
class People:
name: str = 'iworkh'
age: int = 20
admin: bool = True
def __init__(self):
self.admin = False
class MyTestCase(unittest.TestCase):
def test(self):
peo = People()
print(peo.__dict__) # {'admin': False},只有init和塞值的纔會顯示
print(vars(peo)) # {'admin': False}
# 塞值後
peo.name = '沐雨雲樓'
print(peo.__dict__) # 對象的__dict__ , {'admin': False, 'name': '沐雨雲樓'}
print(vars(peo)) # {'admin': False, 'name': '沐雨雲樓'}
print(People.__dict__) # 類的__dict__, 很多,還包括__和自己定義的
2-6.getattribute
屬性訪問攔截器
演示
class People:
name: str = 'iworkh'
password = 'iworkh123'
age: int = 20
def __getattribute__(self, item):
print("call .... __getattribute__")
if item == 'password':
return '保密'
else:
return object.__getattribute__(self, item)
class MyTestCase(unittest.TestCase):
def test(self):
peo = People()
print(peo.name) # iworkh
print(peo.password) # 保密
警告
不要在__getattribute__
方法中調用self.xxxx,會死循環,而應該使用object.__getattribute__(self, item)
2-7.name
__name__
返回類名
演示
class People:
name: str = 'iworkh'
class MyTestCase(unittest.TestCase):
def test_name(self):
print(People.__name__) # 類 People
2-8.屬性操作
屬性操作,無非就塞值、取值、刪除屬性
__setattr__
設置屬性,設置屬性值時,觸發這個方法 (對象.屬性
,setattr
)__getattr__
取屬性,當屬性不存在時,纔會觸發這個方法(對象.屬性
,getattr
)__delattr__
刪除屬性,刪除屬性值時,觸發這個方法(delattr, del)
演示
class People:
name: str = 'iworkh'
password = 'iworkh123'
def __getattr__(self, item):
print('call...__getattr__')
return 'no attribute'
def __delattr__(self, item):
print('call...__delattr__')
object.__delattr__(self, item)
def __setattr__(self, key, value):
print('call...__setattr__')
object.__setattr__(self, key, value)
class MyTestCase(unittest.TestCase):
def test(self):
peo = People()
setattr(peo, 'name', '沐雨雲樓') # 塞值
# 取值
print(peo.name) # 存在, 不會觸發__getattr__
print(peo.sex) # 不存在,調用 __getattr__
print(getattr(peo, 'name')) # 存在, getattr方式 不會觸發__getattr__
print(getattr(peo, 'sex')) # 不存在,getattr方式 調用 __getattr__
# 刪除
peo.password = "test_password" # 塞值
del peo.password
delattr(peo, 'name')
2-9.call
定義__call__
類實例對象可以像調用普通函數那樣,以對象名()
的形式使用。
關於call的可以閱讀下這篇文章: Python __call__詳解
演示
class People:
name: str = 'iworkh'
age: int = 20
def __call__(self, **kwargs):
print('call...__call__')
self.name = kwargs['name']
self.age = kwargs['age']
class MyTestCase(unittest.TestCase):
def test(self):
peo1 = People()
print(peo1.name, peo1.age, sep="......") # iworkh......20
peo1(name='沐雨雲樓', age=22) # 通過對象()方式就能修改值
print(peo1.name, peo1.age, sep="......") # 沐雨雲樓......22
思考:
如何取得類/對中所有屬性或者方法?
dir
:屬性和方法都拿到hasattr
: 屬性和方法都拿到callable
: 屬性爲False, 函數和方法是True
這有實現好的例子 python對象與dict互轉
裏面的
obj_dict_tool.py
類的to_dic
方法
還可以看下這篇文章call方法,使用了__call__
方式
2-10.repr
print
的類如果沒有__str__
,那麼就會調用__repr__
的
演示
class People:
name: str = 'iworkh'
age: int = 20
def __str__(self) -> str:
print("call...__str__")
return "str --> {{'name': {}, 'age': {}}}".format(self.name, self.age)
def __repr__(self):
print("call...__repr__")
return "repr --> {{'name': {}, 'age': {}}}".format(self.name, self.age)
class MyTestCase(unittest.TestCase):
def test(self):
people = People()
print(people)
print(str(people))
將
__str__
代碼註釋後再試下
2-11.比較
對象與對象比較時,得需要比較的方法才能判斷出大小,需要實現下面一些方法
__lt__
: 小於<
__gt__
: 大於>
__le__
: 小於等於<=
__ge__
: 大於等於>=
__eq__
: 等於=
當只定義了一個小於,沒有定義大於時。在使用時也可以使用大於,會根據小於結果取反的。
演示
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def __le__(self, other):
print('call ... le')
return self.age <= other.age
class MyTestCase(unittest.TestCase):
def test(self):
people_san = People('三哥', 30)
people_three = People('張三', 30)
people_four = People('李四', 40)
people_five = People('王五', 50)
print(people_three <= people_four) # True
print(people_five >= people_three) # True
print(people_san >= people_three) # True
print(people_san <= people_three) # True
print(people_three >= people_four) # False
3.list
我們再來看下list的一些屬性
屬性 | 說明 |
---|---|
__add__ |
+ ,返回一個新列表 |
__iadd__ |
+= ,原列表上操作 |
__contains__ |
in ,定義當使用成員測試運算符(in 或 not in)時的行爲 |
__delitem__ |
定義刪除容器中指定元素的行爲 |
__getitem__ |
定義獲取容器中指定元素的行爲 |
__setitem__ |
定義設置容器中指定元素的行爲 |
__iter__ |
for 定義當迭代容器中的元素的行爲 |
__next__ |
for 定義當迭代容器中的元素的行爲 |
__len__ |
定義當被 len() 調用時的行爲 |
__mul__ |
乘觸發,返回新列表 |
__rmul__ |
乘觸發,右操作符,返回新列表 |
__imul__ |
乘觸發,改變自己 |
append |
在列表後面追加元素 |
extend |
在列表後面追加另一個列表的元素 |
insert |
指定位置插入元素 |
copy |
複製 |
count |
list.count(data) 統計列表中出現data的次數 |
index |
list.count(data) 列表中元素的索引 |
pop |
list.pop(index) 彈出指定位置的元素 |
remove |
list.remove(obj) 刪除列表中指定元素 |
clear |
清空list |
reverse |
列表元素倒序輸出 |
sort |
list.sort(reverse=False) 列表元素升序 |
下面只演示模仿方法
使用
dir()
函數來可以打印出有哪些屬性,下面會挑一些常用的演示
演示
class MyTestCase(unittest.TestCase):
def test_attr(self):
print(dir([1,2,3]))
下面就來演示幾個常用的魔法方法,以Alphabet類爲例子,裏面有個data屬性爲list類型,來存字母。
如需要源碼,可以查看。(關鍵代碼都貼在文章裏,不用看也行)
3-1.添加
__add__
: 和+
操作符對應,對象與對象相加,返回一個新列
__iadd__
: 和+=
操作符對應,添加一個元素,在原列表上操作
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __add__(self, other):
print('call...__add__')
return Alphabet(self.data + other.data)
def __iadd__(self, other):
print("call...__iadd__")
self.data += other
return self
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(alphabet.data)
print("*" * 50)
# + 與 __add__
alphabet_add = Alphabet(['m', 'n'])
alphabet_sum = alphabet + alphabet_add # 返回一個新的對象
print(alphabet.data) # 原來的沒有變 ['a', 'c', 'd']
print(alphabet_sum.data) # ['a', 'c', 'd', 'm', 'n']
print("*" * 50)
# += 與 __iadd__
alphabet += 'x'
print(alphabet.data) # 在原對象上操作 ['a', 'c', 'd', 'x']
3-2.包含
__contains__
:是否包含
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __contains__(self, item):
print("call...__contains__")
return item in self.data
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(alphabet.data)
print('a' in alphabet) # True
print('x' in alphabet) # False
3-3.元素操作
__getitem__
:取得元素
__setitem__
:設置元素
__delitem__
:刪除元素
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __getitem__(self, index):
print('call...__getitem__')
return self.data[index]
def __setitem__(self, key, value):
print('call...__setitem__')
self.data[key] = value
def __delitem__(self, key):
print("call...__delitem__")
delattr(self, key)
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(alphabet.data)
print("*" * 30) # ['a', 'c', 'd']
# 取值
print(alphabet[1]) # c 取值 會觸發__getitem__
# 設置值
alphabet[1] = 'b' # 設值 會觸發__setitem__
print(alphabet.data) # ['a', 'b', 'd']
# 刪除
del alphabet['data'] # 刪除 等價於 delattr(alphabet, 'data') 會觸發__delitem__
print(alphabet.data) # []
3-4.迭代
__iter__
:迭代器,返回自己,然後for可以循環調用next方法
__next__
: 每一次for循環都調用該方法(必須存在)
演示
class Alphabet:
data = []
index = -1
def __init__(self, value) -> None:
self.data = value
def __iter__(self):
print("call...__iter__")
return self
def __next__(self):
print("call...__next__")
self.index += 1
if self.index >= len(self.data):
raise StopIteration()
else:
return self.data[self.index]
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(alphabet.data)
# for循環 和 __iter__ 以及 __next__配合
for item in alphabet:
print(item)
3-5.重複
__mul__
: 左操作符相乘,返回新的list,原list不變
__rmul__
: 右操作符相乘,返回新的list,原list不變
__imul__
: 改變原list
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __mul__(self, other):
print("call...__mul__")
return Alphabet(self.data * other)
def __rmul__(self, other):
print("call...__rmul__")
return Alphabet(other * self.data)
def __imul__(self, other):
print("call...__imul__")
return self.data * other
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(alphabet.data)
# __mul__
alphabet_3 = alphabet * 3
print(alphabet.data)
print(alphabet_3.data) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']
print("*" * 50)
# __rmul__
alphabet_r3 = 3 * alphabet
print(alphabet.data)
print(alphabet_r3.data) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']
print("*" * 50)
# __imul__
alphabet *= 3 # 改變了原對象
print(alphabet) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']
3-6.len
__len__
: 這個簡單,但調用len(obj)
會觸發
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __len__(self):
return len(self.data)
class MyTestCase(unittest.TestCase):
def test(self):
list_data = ['a', 'c', 'd']
alphabet = Alphabet(list_data)
print(len(alphabet)) # 3
4.總結
內置屬性(魔法方法)還有很多,只要記得一些常用的即可。
思考
- 1.
__init__
和__new__
的順序、使用 - 2.
__getattribute__
幹嘛的? - 3.
__call__
作用,與callable
內置函數有着怎樣的關係呢? - 4.對象如何才能比較?
__lt__
、__gt__
等 - 5.
__getattr__
、__setattr__
、__delattr__
何時觸發? - 6.
__add__
與__iadd__
區別,何時觸發(+
、+=
)?什麼時候會改變自身,什麼時候會新建對象? - 7.
__getitem__
、__setitem__
、__delitem__
何時觸發? - 8.迭代
__iter__
、__next__
與for-in
循環的配合 - 9.
__mul__
、__rmul__
、__imul__
何時觸發?什麼時候會改變自身,什麼時候會新建對象?
如果上面幾個問題能過回答上,那麼恭喜您,本節內容的精髓您都掌握了。
到這內置函數和內置屬性(魔法方法)都介紹完了,記得要配合 內置函數一起學習哦
5.推薦
能讀到文章最後,首先得謝謝您對本文的肯定,你的肯定是對我們的最大鼓勵。
你覺本文有幫助,那就點個👍
你有疑問,那就留下您的💬
怕把我弄丟了,那就把我⭐
電腦不方便看,那就把發到你📲