天天看点

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-魔法方法详解(上)

你点的每个“在看”,我都认真当成了喜欢