先劃一下重點:
- 6個魔法方法;
- 動態操作屬性的4個函式;
魔法方法
魔法方法的簡單介紹
-
魔法方法的命名規則:方法名(前後各有2個下劃線)。
-
通常情況下,不會主動去呼叫魔法方法,而是在滿足一定的條件下,會自動去呼叫魔法方法。
-
常用的魔法方法有6個,分別是:new、del、str、repr、bytes、call,下面我為大家一一講解。
new方法
- 注意1 :在建立物件的時候,會首先呼叫該方法,init方法還是在 new方法之後被呼叫;
- 注意2 :一個不需要@classmethod修飾的類方法;
- 注意3 :我們只需要知道,在建立物件的時候,new方法比init方法,先呼叫即可。平時沒怎麼用它;
class Person:
# __new__不用@classmethod修飾的一個類方法
# 在建立物件的時候,會首先呼叫該方法
# 如果在該方法中,返回當前類的物件(也即建立物件),接下來會呼叫__init__方法,對物件進行初始化。否則,__init__方法不會得到執行
def __new__(cls):
return super().__new__(cls) # 這行程式碼是建立物件,只是我現在不太明白
Person() # 在我們寫類的時候,不需要寫這個。這裡講述一下,只是想讓我們知道。在建立物件的時候,__new__方法比__init__方法,先呼叫
若一個類中既有new方法,也有init方法,當我們建立物件時候,首先執行new方法。
假如,new方法中沒有return super().new(cls) ,則不會執行init方法。寫上以後,表示再次建立一個物件才會執行init方法。
# 第一段程式碼
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls) # 這行程式碼是建立物件,只是我現在不太明白
def __init__(self):
print("__init__執行")
p = Person()
# 第二段程式碼
class Person:
def __new__(cls):
print("__new__執行")
# return super().__new__(cls) # 假如不返回,當前類的物件,__init__就不會執行
def __init__(self):
print("__init__執行")
p = Person() # 因為,這裡建立的物件,先去執行new方法了
結果如下:
del方法
在銷燬物件的時候(物件指的是我們自己建立的物件),會首先呼叫該方法。
class Person:
def __init__(self):
print("__init__執行")
# 在該方法中,可以執行一些清理工作
def __del__(self):
print("該物件已經被銷燬")
p = Person() # 可以看出,因為沒有物件銷燬,所以不會呼叫該方法
結果如下:
class Person:
def __init__(self):
print("__init__執行")
def __del__(self):
print("該物件已經被銷燬")
p = Person()
del p
結果如下:
str方法
- 注意1 :該方法在使用內建函式(str,format,print)的時候,會自動呼叫;
- 注意2 :該方法末尾,需要返回一個str型別的物件;
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
def __del__(self):
print("該物件已經被銷燬")
# 在使用內建函式(str,format,print)的時候,會自動呼叫該方法
# 該方法需要返回一個str型別的物件
# def __str__(self):
# return "Person型別的物件"
p = Person()
print(p)
結果如下:
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
# 在使用內建函式(str,format,print)的時候,會自動呼叫該方法
# 該方法需要返回一個str型別的物件
def __str__(self):
return "Person型別的物件"
p = Person()
p
結果如下:
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
# 在使用內建函式(str,format,print)的時候,會自動呼叫該方法
# 該方法需要返回一個str型別的物件
def __str__(self):
return "Person型別的物件"
p = Person()
print(p)
結果如下:
repr方法
- 注意1 :該方法在使用內建函式(repr)的時候,會自動呼叫;
- 注意2 :該方法也需要返回一個str型別的物件;
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
def __del__(self):
print("該物件已經被銷燬")
def __str__(self):
return "Person型別的物件"
# __str__ 與 __repr__ 的區別:二者都是返回物件的字串表示。
# 不同的是:__str__ 返回的通常是讓人容易閱讀的格式。__repr__返回的是面向Python直譯器的,通常格式:<內容>
def __repr__(self):
return "<Person class>"
p = Person()
print(str("abc")) # 結果是abc,這個更加接近人閱讀模式
print(repr("abc")) # 結果是'abc',字串本來就是帶引號的,所以這個和python直譯器更相近
結果如下:
bytes方法
該方法在使用內建函式(bytes)的時候,會自動呼叫.
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
def __del__(self):
print("該物件已經被銷燬")
def __str__(self):
return "Person型別的物件"
def __repr__(self):
return "<Person class>"
def __bytes__(self):
return b"byte person class"
p = Person()
print(bytes(p))
結果如下:
call方法
把物件當成函式來使用的時候,會自動呼叫。
class Person:
def __new__(cls):
print("__new__執行")
return super().__new__(cls)
def __init__(self):
print("__init__執行")
def __del__(self):
print("該物件已經被銷燬")
def __str__(self):
return "Person型別的物件"
def __repr__(self):
return "<Person class>"
def __bytes__(self):
return b"byte person class"
# 把物件當成函式來使用的時候,會自動呼叫該方法
def __call__(self):
print("把物件當成函式來使用的時候")
p = Person()
p()
結果如下:
動態操作屬性的4個函式
這4個函式分別是:hasattr、getattr、delattr、setattr,下面我們一一來講述。
hasattr(引數1,引數2)
- 第一個引數 :物件;
- 第二個引數 :屬性名;
- 含義:判斷某個物件,是否存在指定的屬性名。如果存在,返回True,否則,返回False;
class Person():
pass
p = Person()
print(hasattr(p,"name"))
p.name = "3417"
print(hasattr(p,"name"))
結果如下:
getattr(引數1,引數2,引數3)
- 第一個引數 :物件;
- 第二個引數 :屬性名;
- 第三個引數 :當屬性不存在時,返回預設值,這個預設值也是自己設定;
- 含義:返回物件對應的屬性(第二個引數指定的屬性)。傳入2個引數,若不存在,則報錯;穿入3個引數,若不存在,返回第三個引數設定的預設值;
class Person():
pass
p = Person()
print(getattr(p, "name"))
結果如下:
class Person():
pass
p = Person()
p.name = "關注一波?"
print(getattr(p, "name"))
結果如下:
class Person():
pass
p = Person()
print(getattr(p,"age","沒有屬性返回的預設值"))
結果如下:
setattr(引數1,引數2,引數3)
- 第一個引數:物件;
- 第二個引數:屬性名;
- 第三個引數:屬性值;
- setattr(p,"age",20) 相當於 p.age = 20;
class Person():
pass
p = Person()
setattr(p,"age",20)
print(p.age)
結果如下:
delattr(引數1,引數2,引數3)
- 第一個引數:物件;
- 第二個引數:屬性名;
- delattr(p,"name") 相當於 del p.name;
class Person():
pass
p = Person()
setattr(p,"age",20)
delattr(p,"age")
print(hasattr(p,"age"))
結果如下:
注意1 :動態操作屬性沒有直接操作屬性簡便,但是比直接訪問屬性靈活。
注意2 :直接訪問屬性必須在寫程式碼時,就需要知道屬性的名字,而動態操作屬性沒有此限制(這在其他語言中叫做'"反射")。
class Person():
pass
p = Person()
unknown = input("請輸入屬性名:")
value = input("請輸入屬性值:")
setattr(p, unknown, value)
print(getattr(p, unknown))
結果如下: