python魔法方法
1.__init__:
初始化函数,在创建实例对象为其赋值时使用,在__new__方法之后。__init__必须至少有一个参数__self__,就是这个__new__返回的实例,__init__是在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
class Person:
def __init__(self, name):
self.name = name
p1 = Person("liang")
2.__new__:
很多人认为__init__是类的构造函数,其实不太确切,__init__更多的是负责初始化操作,相当于一格项目中的配置文件,__new__才是真正的构造函数,创建并返回一个实例对象,如果__new__只调用了一次,就会得到一格对象。==继承自object的新式类才有__new__这一魔法方法,==__new__至少必须要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,==__new__必须要有返回值,返回实例化出来的实例(很重要),==这点在自己实现__new__时要特别注意,可以返回父类__new__出来的实例,或者直接是object的__new__出来的实例,若__new__没有正确返回当前类cls的实例,那么__init__是不会被调用的,即使是父类的实例也不行。__new__是唯一在实例初始化之前执行的方法,一般用在定义元类时使用。
创建对象的步骤
a) 调用__new__得到一个对象
b) 调用__init__为对象添加属性
c) 将对象赋值给变量
下面是结合了__new__和__init__两个魔法方法的例子
class A:
pass
class B:
def __new__(cls):
print(1, "__new__被调用")
print(2, id(cls))
return object.__new__(A)
def __init__(self):
print(3, "__init__被调用")
b = B()
print(4, b)
print(5, type(b))
print(6, id(A))
print(7, id(B))
输出结果
1 __new__被调用
2 2874488523832
4 <__main__.a object at>
5
6 2874488518168
7 2874488523832
从输出的结果可以看出,__new__中的参数cls和B的id是相同的,表明__new__中默认的参数cls就是B类本身,而在返回时,并没有正确返回当前类cls的实例,而是返回了其父类A的实例,因此__init__这一魔法方法并没有被调用,此时__new__虽然是写在B类中的,但其创建并返回的是一个A类的实例对象。
接下来将return中的参数A改为cls,再看一下运行结果
1 __new__被调用
2 1337840903624
3 __init__被调用
4 <__main__.b object at>
5
6 1337840910232
7 1337840903624
可以看出,当__new__正确返回其当前类cls的实例对象时,__init__被调用了,此时创建并返回的是一个B类的实例对象。
3.__class__:获得已知对象的类
class A:
pass
a = A()
print(a.__class__)
打印的结果
__class__的作用
class A:
count = 0
def add_count(self):
self.__class__.count += 1
a = A()
a.add_count()
print(a.count)
b = A()
b.add_count()
print(b.count)
输出结果
1
2
从运行结果可以看出,虽然a和b是两个不同的A类的实例对象,但采用了__class__之后,分别调用两个对象add_count方法之后,获取到的对象count属性确是在不断累加的。此时self.__class__.count不再是单纯的某个对象私有属性,而是类的所有实例对象的共有属性,它相当于self.A.count。若将self.__class.count += 1变为self.count += 1,此时__class__的效果就十分明显了。
class A:
count = 0
def add_count(self):
self.count += 1
a = A()
a.add_count()
print(a.count)
b = A()
b.add_count()
print(b.count)
输出结果
1
1
4.__str__
在将对象转换成字符串str(对象)测试的时候,打印对象的字符串描述,__str__实际上是被print函数默认调用的,当要print(实际对象)时,默认调用__str__方法,将其字符串描述返回。如果不是要用str()函数转换。当你打印一个类的时候,那么print首先调用的就是类里面定义的__str__。
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return "我是A类的实例对象"
a = A("liang")
print(a)
输出结果
我是A类的实例对象
5. __repr__
如果说__str__体现的是一种可读性,是给用户看的,那么__repr__方法体现的则是一种准确性,是给开发人员看的,它对应的是repr()函数,重构__repr__方法后,在控制台直接敲出实例对象的名称,就可以按照__repr__中return的值进行显示
class A:
def __str__(self):
return "我是A类的实例对象***"
def __repr__(self):
return "我是A的实例对象"
Note:
打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示
__repr__用于所有其它的环境中,用于交互模式下提示回应以及repr()函数,它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发这详细的显示
6.__del__
对象在程序运行结束之后进行垃圾回收的时候调用这个方法,来释放资源。此时,该方法是被自动调用的。除非有特殊要求,一般不要重写。在关闭数据库连接对象的时候,可以在这里,释放资源
class A:
def __init__(self, name):
print("__init__方法被调用")
self.name = name
def __del__(self):
print("%s被销毁了" % self.name)
a = A("liang")
b = A("xin")
print("结束")
输出结果
__init__方法被调用
__init__方法被调用
结束
liang被销毁了
xin被销毁了
可以看到在程序结束之后,__del__默认被调用了两次,分别对实例对象a,b进行了垃圾回收,因为此时创建的实例已经没有对象再指向它了
当有额外的变量指向实例时
import time
class A:
def __init__(self, name):
self.name = name
print("__init__被调用")
def __del__(self):
print("%s被销毁了" % self.name)
a = A("liang")
b = a
del a
time.sleep(2)
print("***")
del b
time.sleep(3)
print("结束")
输出结果
__init__被调用
***
liang被销毁了
结束
当销毁a时,因为还有变量b指向该对象,所以并没有调用该实例的__del__方法。当销毁b后,即引用计数等于0时,垃圾回收自动调用
7.__getattribute__
属性访问拦截器,在访问实例属性时自动调用。在python中,类的属性和方法都理解为属性,且均可以通过__getattribute__获取。当获取属性时,相当于对属性进行重写,直接return object.__getattribute__(self, *args, **kwargs)或者根据判断return所需要的重写值,如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可。如果不加的话,返回的时函数引用地址
class A:
def __init__(self, name):
self.name = name
self.money = 1
def __getattribute__(self, obj):
if obj == "name":
return "chen"
else:
return object.__getattribute__(self, obj)
a = A("liang")
print(a.name)
print(a.money)
输出结果
chen
1
在创建实例对象s并对其初始化的时候,name的值被设置为”liang“, money的值被设置为1。在访问a的name属性时,因为重写了__getattribute__方法,对要访问的属性做了一格拦截和判断,此时__getattribute__方法中的参数obj就是要访问的属性,若要访问name属性,则对该属性进行重写,返回了一个不同的字符串。而访问其它属性时,调用了父类中的__getattribute__方法,返回正确的属性值
8. __bases__
获取指定类的所有父类构成元素,使用方法为类名.__bases__
class A:
pass
class B(A):
pass
class C:
pass
class D(B, C):
pass
print(D.__bases__)
输出结果
(, )
9.__mro__
显示指定类的所有继承脉络和继承顺序,加入这个指定的类不具有某些方法和属性,但与其有血统关系的类中具有这些属性和方法,则在访问这个类本身不具有这些方法和属性时,会按照__mro__显示出来的顺序一层一层向后查找,直到找到位置
class A:
pass
class B(A):
pass
class C:
pass
class D(B, C):
pass
print(D.__mro__)
输出结果
(, , , , )
10. __call__
具有__call__魔法方法的对象可以像调用函数一样直接被调用
class Person:
def __init__(self, name):
self.name = name
def __call__(self):
print("我的名字是%s" % self.name)
p1 = Person("chenliang")
p1()
输出结果: 我的名字是chenliang
11. __slots__
可以限制实例对象的属性和方法,但是对类不起作用
class Person:
__slots__ = ["name", "age"]
def __init__(self, name, age):
self.name = name
self.age = age
self.gender = "男"
p1 = Person("chenliang", 26)
执行报出错误
Traceback (most recent call last):
File "D:/project/Python/year_2019/month_12/day_14/test_12.py", line 11, in
p1 = Person("chenliang", 26)
File "D:/project/Python/year_2019/month_12/day_14/test_12.py", line 8, in __init__
self.gender = "男"
AttributeError: 'Person' object has no attribute 'gender'
因为我们限制了该对象属性只有name和age,所以初始化的时候gender不能被声明
Note:使用__slots__可以让节省内存,但是对于小级别的实例不值得使用(百万级别以下)
12.__len__
定义当被len()方法调用时的行为
class A:
def __init__(self, number):
self.number = number
def __len__(self):
return self.number
a = A(10)
print(len(a))
输出结果:10
13. __bytes__
定义被bytes()方法调用时的行为
class B:
def __init__(self, name):
self.name = name
def __bytes__(self):
return self.name.encode("utf-8")
b = B("chenliang")
print(bytes(b))
输出结果: b"chenliang"
14.__hash__
定义被hash()调用时的行为
class A:
def __hash__(self):
return 1
a = A()
print(hash(a))
输出结果: 1
15. __bool__
定义被bool()调用时的行为,应该返回True或False
class A:
def __init__(self, number):
self.number = number
def __bool__(self):
if self.number > 0:
return True
else:
return False
a = A(1)
b = A(-1)
print(bool(a))
print(bool(b))
输出结果
True
False
16. __format__
待完善
17. __getattr__
定义当用户试图获取一个不存在的属性时的行为
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
if item == "age":
return 0
a = A("liang")
print(a.name)
print(a.age)
输出结果
liang
0
当我们访问不存在的属性age时,因为没有定义该属性,所以最后执行__getattr__方法
18. __getattribute__
定义当一个属性被访问时的行为
class AboutAttr(object):
def __init__(self, name):
self.name = name
def __getattribute__(self, item):
try:
return super(AboutAttr, self).__getattribute__(item)
except KeyError:
return "default"
except AttributeError as ex:
print(ex)
# return self.__getattr__(item)
def __getattr__(self, item):
return "default"
at = AboutAttr("test")
print(at.name)
print(at.not_existed)
输出结果
test
'AboutAttr' object has no attribute 'not_existed'
None
由于注释掉了return self.__getattr__(item),所以最终返回了None。没注释掉时最后返回的是'default'
19. __setattr__
定义当一个属性被设置时的行为
class A:
def __init__(self, name):
self.name = name
def __setattr__(self, name, value):
print("%s被设置成了%s" % (name, value))
super().__setattr__(name, value)
a = A("liang")
a.age = 26
print(a.age)
输出结果
name被设置成了liang
age被设置成了26
26
20. __delattr__
定义当一个属性被删除时的行为
class A:
def __init__(self, name):
self.name = name
def __delattr__(self, item):
super().__delattr__(item)
print("%s属性被删除了" % item)
a = A("liang")
print(a.name)
del a.name
# print(a.name)
输出结果
liang
name属性被删除了
21.__dir__
定义被dir()调用时的行为
class A:
def __init__(self, name):
self.name = name
def __dir__(self):
print("__dir__方法被调用了")
return super().__dir__()
a = A("chenliang")
print(dir(a))
输出结果
dir函数被调用
['__class__', '__delattr__',......
22. __get__
定义当描述符的值被取得时的行为
class A:
a = "abc"
def __get__(self, instance, owner):
print("__get___被调用", instance, owner)
return self
class B:
b = A()
a1 = A()
b1 = B()
print(a1.a)
print(b1.b.a)
输出结果
abc
__get___被调用 <__main__.b object at>
abc
当注释掉return self时
class A:
a = "abc"
def __get__(self, instance, owner):
print("__get___被调用", instance, owner)
# return self
class B:
b = A()
a1 = A()
b1 = B()
print(a1.a)
print(b1.b.a)
输出结果
abc
Traceback (most recent call last):
__get___被调用 <__main__.b object at>
File "D:/project/python/year_2019/month_12/day_15/test_01.py", line 16, in
print(b1.b.a)
AttributeError: 'NoneType' object has no attribute 'a'
23.__set__
定义当描述符的值被改变时的行为
24.__delete__
定义当描述符的值被删除时的行为
==22、23、24这块关于描述符的地方,还不是特别理解,等待补充==
25. __lt__
定义小于号的行为lt是less than 的缩写
class A:
def __init__(self, number):
self.number = number
def __lt__(self, other):
if self.number < other.number:
return True
else:
return False
a1 = A(2)
a2 = A(3)
a3 = A(-1)
print(a1 < a2)
print(a1 < a3)
26. __le__
le: less equal,小于等于,即定义小于等于号的行为
27.__eq__
eq:equal,等于,即定义等于号的行为
28. __ne__
ne: not equal, 不等于,即定义不等于号的行为
29. __gt__
gt: grant than,大于,即定义大于号的行为
30.__ge__
ge: grant equal,大于等于,即定义大于等于的行为
==26、27、28、29、30和25定义相似,同属于比较运算符的行为,这里不再赘述==
31.__add__
定义加法的行为, +
class A:
def __init__(self, number):
self.number = number
def __add__(self, other):
return self.number + other
a = A(2)
b = 3
print(a + b)
输出结果: 5
32.__sub__
定义减法的行为, -
33.__mul__
定义乘法的行为, *
34.__truedir__
定义真除法的行为,/
35. __floordir__
定义整除法的行为, //
36. __mod__
定义取模算法的行为, %
待续