python 魔法函数 __add___python魔法方法

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__

定义取模算法的行为, %

待续


版权声明:本文为weixin_42139460原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。