天天看點

Python-魔法方法詳解(上)

Python-魔法方法詳解(上)

作者 | 奔跑的豆子_

來源 | CSDN部落格

new

new__是在一個對象執行個體化的時候所調用的第一個方法,它的第一個參數是它的類,其他參數用于傳遞給_init_方法,_new_決定是否要使用_init_方法,因為_new_可以調用其他類的構造方法或者傳回别的執行個體對象來作為本類的執行個體對象,如果_new_沒有傳回執行個體對象,那麼_init_将不會被調用,__new方法主要是當你繼承一些不可變的class時(int, str, tuple),提供給你一個自定義這些類的執行個體化過程的途徑。如下:

class Student(int):    def __new__(cls, value):        return super(Student, cls).__new__(cls, abs(value))s = Student(-3)print(s)

    def __new__(cls, value):
        return super(Student, cls).__new__(cls, abs(value))

s = Student()
print(s)
           

通過重載new__方法,我們實作了我們想要的需求!我們還可以通過重載__new方法,可以簡單的實作單例模式:

class Student(object):	
	
    instance = None	
	
    def __new__(cls, *args, **kwargs):	
	
        if cls.instance is None:	
            cls.instance = super(Student, cls).__new__(cls, *args, **kwargs)        return cls.instances1 = Student()s2 = Student()s1.value = 20print(s1, s2)print(s1.value, s2.value)print(s1 is s2)############## 列印結果如下 ##############<__main__.Student object at 0x1033a1c88> <__main__.Student object at 0x1033a1c88>20 20True
           

init

構造器,當一個執行個體被建立時調用的初始化方法

del

析構器,當一個執行個體被銷毀時調用的方法

call

允許一個類的執行個體像函數一樣被調用。舉個栗子:

class Student(object):	
	
    def __call__(self, value):	
	
        print(value)	
s = Student()s(3)
           

運作之後,将會列印數字3!

len

調用len()将會觸發,須傳回一個數值,舉個栗子:

class Student(object):	
	
    def __len__(self):	
	
        print("__len__")	
        return 0s = Student()print(len(s))############## 列印結果如下 ##############__len__
           

repr

當程式員直接列印該對象時,系統将會輸出該對象的“自我描述”資訊,用來告訴外界該對象具有的狀态資訊,也可以通過内置函數repr()函數來擷取對象的資訊。舉個栗子:

class Student(object):	
	
    pass	
s = Student()print(s)############## 列印結果如下 ##############<__main__.Student object at 0x1033d3390>class Student(object)    def __repr__(self):        return "student"s = Student()print(s)############## 列印結果如下 ##############student
           

str

repr__與_str_都是用于顯示的,_str_是面向使用者的,__repr是面向程式員的。舉個栗子,在互動環境中效果如下:

>>> class Student(object):	
	
>>>	
>>>     def __repr__(self):>>>         return "__repr__">>>>>>     def __str__(self):>>>         return "__str__">>>>>> s = Student()>>> s__repr__>>> print(s)__str__>>> repr(s)'__repr__'>>> str(s)'__str__'
           

列印操作首先會嘗試str__和str内置函數,它通常會傳回一個友好顯示;_repr_用于其他所有的環境中,互動式環境中提示回應或調用repr()函數,将會傳回_repr_資訊,當使用print()或str()函數,它通常會看有無_str_,如果無,才會顯示__repr的資訊。

bytes

調用bytes()将會觸發,須傳回一個byte,舉個栗子:

class Student(str):	
	
    def __bytes__(self):	
	
        print("__bytes__")	
        return b"__bytes__"s = Student()bytes(s)
           

hash

hash在下面兩種場景中觸發使用:

在使用内置函數hash()時hash類型的集合對自身成員的hash操作:set、frozenset、dict

舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __hash__(self):        print("{} __hash__".format(self.name))        return hash(self.name)s = Student("laozhang")print(hash(s))############## 列印結果如下 ##############laozhang __hash__-8362428577884394890
           

hash類型的集合:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __hash__(self):        print("{} __hash__".format(self.name))        return hash(self.name)s1 = Student("laozhang")s2 = Student("laowang")s3 = Student("laoli")s4 = Student("laozhang")data = {s1, s2, s3, s4}print(data)############## 列印結果如下 ##############laozhang __hash__laowang __hash__laoli __hash__laowang __hash__{<__main__.Student object at 0x103bb2208>, <__main__.Student object at 0x103bb21d0>, <__main__.Student object at 0x103bb2198>, <__main__.Student object at 0x103bad748>}
           

我們知道,集合有去重效果,可是laozhang出現了兩次,并未達到我們的效果。這是因為s1與s2雖然name相同,但是分屬兩個對象,而判斷是否是同一個對象的方法是eq__(),預設的_eq_()會比較對象之間的記憶體位址,以此來判斷是否是同一個對象。s1、s2記憶體位址不同,是以是兩個對象!我們隻需重寫一下__eq()方法,使其強制變為同一個對象,如下:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __hash__(self):        print("{} __hash__".format(self.name))        return hash(self.name)    def __eq__(self, other):        print("{} __eq__ {}".format(self.name, other.name))        return self.name == other.names1 = Student("laozhang")s2 = Student("laowang")s3 = Student("laoli")s4 = Student("laozhang")data = {s1, s2, s3, s4}print(data)############## 列印結果如下 ##############laozhang __hash__laowang __hash__laoli __hash__laozhang __hash__laozhang __eq__ laozhang{<__main__.Student object at 0x103bad710>, <__main__.Student object at 0x103ba0c88>, <__main__.Student object at 0x103bb2160>}
           

值得注意的是,如果類重寫了eq__()方法,而沒有重寫__hash()方法,那麼将無法進行hash操作,将會報錯。如下:

from collections.abc import Hashable	
class Student(object):    def __init__(self, name):        self.name = nameclass Teacher(object):    def __init__(self, name):        self.name = name    def __eq__(self, other):        print("__eq__")        return self.name == other.names = Student("laozhang")t = Teacher("laozhang")print(isinstance(s, Hashable))print(isinstance(t, Hashable))print(hash(s))print(hash(t))############## 列印結果如下 ##############TrueFalse271822193Traceback (most recent call last):  ...TypeError: unhashable type: 'Teacher'
           

這是因為重寫eq__()方法後會預設把__hash指派為None。還是以上面類為例,如下:

s = Student("laozhang")	
t = Teacher("laozhang")print(s.__hash__)print(t.__hash__)############## 列印結果如下 ##############<method-wrapper '__hash__' of Student object at 0x1033ad748>None
           

使用者定義的類預設都有eq__()和__hash()方法,這是從object中繼承的,如果你不重寫任何一個,那麼同一個類的執行個體x與y來說,x is y、x == y和hash(x) == hash(y)會同時成立或同時不成立!

bool

當調用bool()函數将會觸發bool__()方法,傳回True或者False。其實bool()的本質,就是調用_bool_()的結果,如果執行個體不存在_bool_()方法,python則将會調用__len()方法,如果為0,那麼bool()将會得到False,否則傳回True。舉個栗子:

class A(object):	
	
    pass	
	
class B(object):	
	
    def __bool__(self):	
	
        print("__bool__")	
        return Trueclass C(object):    def __len__(self):        print("__len__")        return 0a = A()b = B()c = C()print(bool(a))print(bool(b))print(bool(c))############## 列印結果如下 ##############True__bool__True__len__False
           

預設情況下,我們自定義的類的執行個體都會被認為是真的,除非自定義的類中bool__()或__len()方法有其他實作

__format__	
	
當format()被調用時觸發。	
舉個栗子:class Student(object):    def __init__(self, name):        self.name = name    def __format__(self, format_spec):        print("__format__")        if format_spec == "":            return str(self)        result = format_spec.replace("{}", self.name)        return results = Student("laozhang")print(format(s, "{}"))print("{}".format(s))############## 列印結果如下 ##############__format__laozhang__format__<__main__.Student object at 0x102bc0c88>
           

getattr

當類的執行個體讀取一個不存在的屬性(包括類屬性與執行個體屬性)時,将會觸發,如果存在将不觸發。舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
	
        self.name = name	
    def __getattr__(self, item):        return "__getattr__"s = Student("laowang")print(s.name)print(s.age)print(getattr(s, "addr"))############## 列印結果如下 ##############laowang__getattr____getattr__
           

getattribute

當使用類的執行個體讀取該類的屬性(包括類屬性與執行個體屬性)時,将會觸發,無論該屬性存在與否。舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
	
        self.name = name	
    def __getattribute__(self, item):        return "__getattribute__"s = Student("laowang")print(s.name)print(s.age)print(getattr(s, "addr"))############## 列印結果如下 ##############__getattribute____getattribute____getattribute__
           

另外,如果同時重寫了getattribute__()、_getattr_()方法,并且有自定義的傳回,那麼正常情況下__getattr()方法不會再觸發,除非顯示調用或引發AttributeError錯誤!如下:

class Student(object):	
	
    def __init__(self, name):	
	
        self.name = name	
    def __getattr__(self, item):        return "__getattr__"    def __getattribute__(self, item):        return "__getattribute__"s = Student("laowang")print(s.name)print(s.age)print(getattr(s, "addr"))############## 列印結果如下 ##############__getattribute____getattribute____getattribute__
           

值得注意的是,無論是在getattr__()或者_getattribute_(),都切記不要在方法内部調用self.item,否則将會進入遞歸循環,__getattribute()中調用其他屬性也會使自身進入遞歸循環!!!為了避免無限循環,我們可以擷取屬性的方法指向一個更高的超類,如下:

def __getattribute__(self, item):	
    return super(Student, self).__getattribute__(item)
           

setattr

當使用類的執行個體為屬性進行指派時,将會觸發,舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __setattr__(self, key, value):        print("__setattr__")        super(Student, self).__setattr__(key, value)s = Student("laowang")s.age = 20setattr(s, "addr", "深圳")############## 列印結果如下 ##############__setattr____setattr____setattr__
           

值得注意的是,切勿在setattr()方法中操作self.key = value,這樣将會進入遞歸循環!

delattr

當類的執行個體中的一個屬性被删除時,将會觸發(無論屬性存不存在)。舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __delattr__(self, item):        print("__delattr__")s = Student("laowang")delattr(s, "name")delattr(s, "addr")############## 列印結果如下 ##############__delattr____delattr__
           

dir

當dir()函數被調用是,将會觸發。舉個栗子:

class Student(object):	
	
    def __init__(self, name):	
        self.name = name    def __dir__(self):        print("__dir__")        return super(Student, self).__dir__()s = Student("laowang")print(dir(s))############## 列印結果如下 ##############__dir__['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
           

get

如果class定義了它,則這個class就可以稱為descriptor。owner是所有者的類,instance是通路descriptor的執行個體,如果不是通過執行個體通路的,而是通過類通路的,那麼instance則為None。舉個栗子:

class Teacher(object):	
    name = "張老師"    def __get__(self, instance, owner):        print("__get__", instance, owner)        return selfclass Student(object):    t = Teacher()s = Student()t = Teacher()print(s.t)print(Student.t)print(s.t.name)print(Student.t.name)print(t)print(t.name)############## 列印結果如下 ##############__get__ <__main__.Teacher object at 0x1033d24e0> <__main__.Student object at 0x1033d2518> <class '__main__.Student'><__main__.Teacher object at 0x1033d24e0>__get__ <__main__.Teacher object at 0x1033d24e0> None <class '__main__.Student'><__main__.Teacher object at 0x1033d24e0>__get__ <__main__.Teacher object at 0x1033d24e0> <__main__.Student object at 0x1033d2518> <class '__main__.Student'>張老師__get__ <__main__.Teacher object at 0x1033d24e0> None <class '__main__.Student'>張老師<__main__.Teacher object at 0x1033d2550>張老師
           

通過列印結果可以得知,descriptor的執行個體通路自己是不會觸發get方法的!

将上面代碼修改成為如下:

class Teacher(object):	
	
    name = "張老師"	
	
    def __get__(self, instance, owner):	
	
        print("__get__", instance, owner)	
        return selfclass Student(object):    def __init__(self):        self.t = Teacher()s = Student()print(s.t)print(s.t.name)############## 列印結果如下 ##############<__main__.Teacher object at 0x1033ad748>張老師
           

從結果可以得知,如果descriptor為某個類的執行個體屬性,那麼調用他将不會觸發get方法

set

set__方法的觸發條件與__get類似,舉個栗子:

class Teacher(object):	
	
    name = "張老師"	
	
    def __set__(self, instance, value):	
	
        print("__set__", self, instance, value)	
class Student(object):    t = Teacher()s = Student()s.t = "laowang"Student.t = "laozhang"############## 列印結果如下 ##############__set__ <__main__.Teacher object at 0x102bad748> <__main__.Student object at 0x102bb2198> laowang
           

從列印結果可知,如果通路descriptor的是類的執行個體,會觸發;如果是通路descriptor的是類,那麼将不會觸發!同樣,如果descriptor為某個類的執行個體屬性,通路時,同樣也不會觸發!即下面這種情況時不會觸發的:

class Teacher(object):	
	
    name = "張老師"	
	
    def __set__(self, instance, value):	
        print("__set__", self, instance, value)class Student(object):    def __init__(self):        self.t = Teacher()s = Student()s.t = "laowang"
           

delete

delete__方法的觸發條件與__set,調用删除所有者類的執行個體的屬性觸發。舉個栗子:

class Teacher(object):	
	
    name = "張老師"	
	
    def __delete__(self, instance):	
	
        print("__delete__", self, instance)	
class Student(object):    t = Teacher()# del Student.t     # 不觸發,s = Student()del s.t############## 列印結果如下 ##############__delete__ <__main__.Teacher object at 0x1033a0c50> <__main__.Student object at 0x1033a0c88>
           

同樣,如果descriptor為某個類的執行個體屬性,删除時,同樣也不會觸發!即下面這種情況時不會觸發的:

class Teacher(object):	
	
    name = "張老師"	
	
    def __set__(self, instance, value):	
	
        print("__set__", self, instance, value)	
class Student(object):    def __init__(self):        self.t = Teacher()s = Student()del s.t
           

lt

定義小于号的行為,将會觸發執行個體的lt()方法。舉個栗子:

class Student(object):	
	
    def __init__(self, name, age):	
        self.name = name        self.age = age    def __lt__(self, other):        print("__lt__", self.age, other.age)        return self.age < other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 < s2)############## 列印結果如下 ##############__lt__ 18 20True
           

gt

定義大于号的行為,将會觸發執行個體的gt()方法。舉個栗子:

class Student(object):	
	
    def __init__(self, name, age):	
        self.name = name        self.age = age    def __gt__(self, other):        print("__gt__", self.age, other.age)        return self.age > other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 > s2)############## 列印結果如下 ##############__lt__ 18 20False
           

再看如下栗子:

class Student(object):	
	
    def __init__(self, name, age):	
        self.name = name        self.age = age    def __gt__(self, other):        print("__gt__", self.age, other.age)        return self.age > other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 < s2)############## 列印結果如下 ##############__gt__ 20 18Trueclass Student(object):    def __init__(self, name, age):        self.name = name        self.age = age    def __lt__(self, other):        print("__lt__", self.age, other.age)        return self.age < other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 > s2)############## 列印結果如下 ##############__lt__ 20 18Falseclass Student(object):    def __init__(self, name, age):        self.name = name        self.age = age    def __gt__(self, other):        print("__gt__", self.age, other.age)        return self.age > other.age    def __lt__(self, other):        print("__lt__", self.age, other.age)        return self.age < other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 > s2)print(s1 < s2)############## 列印結果如下 ##############__gt__ 18 20False__lt__ 18 20True
           

從栗子中可以看出,如果調用x < y,那麼首先将會尋找x的lt__()方法,如果x未重寫它,那麼将會尋找y的_gt_()方法!同理,如果調用x > y,将會首先尋找x的_gt_()方法,如果x未重寫它,那麼将會尋找y的__lt()方法!!!

ge

定義大于或等于号的行為,将會觸發ge()方法。舉個栗子:

class Student(object):	
	
    def __init__(self, name, age):	
        self.name = name        self.age = age    def __ge__(self, other):        print("__ge__", self.age, other.age)        return self.age >= other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 >= s2)############## 列印結果如下 ##############__ge__ 18 20False
           

le

定義小于或等于号的行為,将會觸發le()方法。舉個栗子:

class Student(object):	
	
    def __init__(self, name, age):	
        self.name = name        self.age = age    def __le__(self, other):        print("__le__", self.age, other.age)        return self.age <= other.ages1 = Student("laowang", 18)s2 = Student("laozhang", 20)print(s1 <= s2)############## 列印結果如下 ##############__le__ 18 20True
           

與gt__()、_lt_()方法一樣,如果調用x <= y,首先會尋找x的_le_()方法,如果x未重寫它,那麼将會尋找y的_ge_()方法!同理,如果調用x >= y,将會首先尋找x的_ge_()方法,如果x未重寫它,那麼将會尋找y的__le()方法!!!

eq

定義等于号的行為,即當使用==操作時,将會觸發eq,舉個栗子:

class Student(object):	
	
    def __eq__(self, other):	
	
        print("__eq__")	
        return self.__dict__ == other.__dict__s1 = Student()s2 = Student()print(s1 == s2)     # 觸發__eq__print(s1 is s2)     # 不觸發############## 列印結果如下 ##############__eq__TrueFalse
           

ne

定義不等于号的行為,即當使用!=操作時,将會觸發ne,舉個栗子:

class Student(object):	
	
    def __ne__(self, other):	
	
        print("__ne__")	
        return self.__dict__ == other.__dict__s1 = Student()s2 = Student()print(s1 != s2)     # 觸發__eq__print(s1 is not s2) # 不觸發############## 列印結果如下 ##############__ne__TrueTrue
           

原文:

https://blog.csdn.net/y472360651/article/details/94580032 

精彩推薦

文本挖掘有什麼用?它和NLP有關系嗎?工程中,文本挖掘怎麼做?有哪些方法?文本挖掘怎麼學?要掌握哪些必備技能和工具(包)?

掃碼回複:文本挖掘,加入課程交流群

Python-魔法方法詳解(上)

推薦閱讀:

  • 200行代碼實作一個滑動驗證碼

  • 爬蟲到底違法嗎?這位爬蟲工程師給出了答案
  • 收藏!本、碩、博、程式員必備神器
  • 阿裡巴巴楊群:高并發場景下Python的性能挑戰
  • 24式,加速你的Python

  • Python從入門到精通,這篇文章為你列出了25個關鍵技術點(附代碼)
  • 500行Python代碼打造刷臉考勤系統
Python-魔法方法詳解(上)

你點的每個“在看”,我都認真當成了喜歡