Python基礎(二十一):物件導向“類”第四課——魔法方法

pure3417發表於2021-04-13

先劃一下重點:

  • 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))

結果如下:

相關文章