魔法方法
Python里面有一种特殊的方法,叫做魔法方法。Python的类里提供的,是由两个下划线开始,再以两个下划线结束的方法。
魔法方法不需要手动调用,会在恰当的时候就会被激活,自动执行。
魔法方法的名字都是系统规定好的,不能乱写,在合适的时候自己调用。
1.__init__方法
_init_()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__ 方法进行改造。
class Student(object):def __init__(self, name): # 重写了___init__魔法方法,设置对象的属性# 在创建对象时,会自动调用这个方法。print('__init__方法被调用了')self.name = namedef play_game(self):print('{}正在玩游戏'.format(self.name))s1 = Student('张三') #创建对象时,必须要指定name属性的值print(s1.name) # 张三s1.play_game() # 张三正在玩游戏
运行结果:
__init__方法被调用了
张三
张三正在玩游戏
注意:
_init_()方法在创建对象时默认被调用,不需要手动调用。_init_()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。
2.__del__方法
既然有创建对象时的魔法方法调用,就会有删除对象时的魔法方法调用,_del_()方法就是当对象被销毁时,会自动调用这个方法。
class Student(object):def __init__(self, name):print('__init__方法被调用了')self.name = namedef __del__(self):# 当对象被销毁时,会自动调用这个方法print('__del__ 方法被调用了')def play_game(self):print('{}正在玩游戏'.format(self.name))s1 = Student('张三')del s1
运行结果:
__init__方法被调用了
del方法被调用了
3.__str__方法
__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。
调用对象的 __str__方法,默认会打印类名和对象的地址名。
class Student(object):def __init__(self, name):self.name = names1 = Student('张三')print(s1) # <__main__.Student object at 0x00000298D1161808>
那我们如何得到s1的值呢?其实想要修改对象的输出结果,我们就需要重写__str__方法。
class Student(object):def __init__(self, name):self.name = namedef __str__(self):return '姓名:{}'.format(self.name)s1 = Student('张三')print(s1) # 姓名:张三
4.__repr__方法
__repr__方法和__str__方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写__str__方法,它会自动来查找__repr__方法。如果这两个方法都没有,会直接打印这个对象的内存地址。
class Student(object):def __init__(self, name):self.name = namedef __repr__(self):return '姓名:{}'.format(self.name)s1 = Student('张三')print(s1) # 姓名:张三
倘若__repr__方法和__str__方法同时出现,会发生什么情况呢???
class Student(object):def __init__(self, name):self.name = namedef __repr__(self):return 'hello world'def __str__(self):return '姓名:{}'.format(self.name)s1 = Student('张三')print(s1) # 姓名:张三
从上面的实例可知,若两种方法同时出现,会选择调用__str__方法。
如果还想在有__str__方法的情况下,调用__repr__方法,我们可以调用内置函数repr或者手动调用__repr__方法。
class Student(object):def __init__(self, name):self.name = namedef __repr__(self):return 'hello world'def __str__(self):return '姓名:{}'.format(self.name)s1 = Student('张三')print(s1) #自动调用__str__方法print(repr(s1)) # 调用内置函数 repr 会触发对象的 __repr__ 方法print(s1.__repr__()) # 手动调用__repr__魔法方法
运行结果:
姓名:张三
hello world
hello world
注意:
如果不做任何的修改,直接打印一个对象,是文件的name.类型 内存地址当打印一个对象的时候,会调用这个对象的str或者repr方法如果两个方法都写了,选择str如果在两个方法都写了的情况下,调用__repr__方法,我们可以调用内置函数repr或者手动调用__repr__方法。
5.__call__方法
对象后面加括号,触发执行。
class Student(object):def __init__(self, name):self.name = namedef __call__(self, *args, **kwargs):print('args={},kwargs={}'.format(args, kwargs))s1 = Student('张三')s1(1, 2, 3, age=18, score=99) # args=(1, 2, 3),kwargs={'age': 18, 'score': 99}
6.比较运算符相关的魔法方法
class Student(object):def __init__(self, name, score):self.name = nameself.score = scores1 = Student('zhangsan', 90)s2 = Student('zhangsan', 90)
s1和s2是否是同一个对象?
比较两个对象是否是同一个对象,我们需要比较的是内存地址
print('0x%X' % id(s1)) # 0x16AA83BB408print('0x%X' % id(s2)) # 0x16AA83BB448
由上可知,它们的内存地址不一样,两个对象不是同一个对象。
print('s1 is s2', s1 is s2) # s1 is s2 Falseprint(s1 == s2) # False
is 身份运算符,比较两个对象的内存地址
== 会调用对象的eq方法,如果不重写,默认比较依然是内存地址,如果重写方法,便可以获取这个方法的比较结果。
class Student(object):def __init__(self, name, score):self.name = nameself.score = scoredef __eq__(self, other):return self.name == other.name and self.score == other.scores1 = Student('zhangsan', 90)s2 = Student('zhangsan', 90)print( s1 == s2) # True
其它比较运算符,也是如此。
class Student(object):def __init__(self, name, score):self.name = nameself.score = scoredef __eq__(self, other):return self.name == other.name and self.score == other.score#def __ne__(self, other):# 使用 != 运算符会自动调用这个方法def __lt__(self, other):return self.score < other.scoredef __gt__(self, other):return self.score > other.scoredef __le__(self, other): return self.score <= other.scoredef __ge__(self, other): return self.score >= other.scores1 = Student('zhangsan', 90)s2 = Student('zhangsan', 90)s3 = Student('lisi', 94)s4 = Student('wangwu', 99)print(s1 == s2) #Trueprint(s1 != s2) #Falseprint(s1 > s3) #Falseprint(s1 >= s4) #Falseprint(s1 < s4) #Trueprint(s1 <= s3) #True
7.算数运算符相关的魔法方法
class Student(object):def __init__(self, name, score):self.name = nameself.score = scoredef __add__(self, other):return self.score + otherdef __sub__(self, other):return self.score - otherdef __mul__(self, other):return self.name * otherdef __truediv__(self, other):return self.score / otherdef __mod__(self, other):return self.score % otherdef __pow__(self, power, modulo=None):return self.score ** powers = Student('zhangsan', 95)print(s + 1) # 96print(s - 2) # 93print(s * 2) # zhangsanzhangsanprint(s / 5) # 19.0print(s % 5) # 0print(s ** 2) # 9025
8.类型转换相关的魔法方法
class Student(object):def __init__(self, name, score):self.name = nameself.score = scoredef __int__(self):return self.scoredef __float__(self):return self.score * 1.0def __str__(self):return self.namedef __bool__(self):return self.score > 60s1 = Student('zhangsan', 90)print(int(s1)) # 90print(float(s1)) # 90.0print(str(s1)) # zhangsanprint(bool(s1)) # True
9.容器方法
__ len__ -> len计算长度
__ iter__ -> forfor循环
__ contains__ -> in判断是否存在
__ getitem__ 对 string、 bytes、list、tuple、dict 有效 下标求值
__ setitem__ 对 list、dict 有效添加、修改
需求:求出k列表后三位数
k = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]print(k.__getitem__(-3:))) # 报错
这里可以使用slice切片
k = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]print(k.__getitem__(slice(7, 10))) # [8, 9, 0]
10.上下文管理with
__ enter__ 进⼊ with 代码块前的准备操作
__ exit__ 退出时的善后操作
⽂件对象、线程锁、socket 对象等都可以使⽤ with 操作。
为什么要使用with来操作???因为使用with可以自动关闭文件,即便是代码程序报错,其也能关闭文件。
class A:def __enter__(self):print('exec enter')return selfdef __exit__(self, a1, a2, a3):print('exec exit')print('a1:', a1)print('a2:', a2)print('a3:', a3)with A() as a:print('a = ', a)raise ValueError('errorerror')
运行结果:
11.属性相关的方法
__ dict__、__ setattr__、__ getattribute__、__ getattr__
内建函数:setattr()、getattr()、hasattr()
对象的属性都存放在__ dict__中,类属性在类对象中保存;普通的对象是由类创建的,类是由元类创建的。
class A:def __init__(self):self.x = 123self.y = 456a = A()a.z = 789print(a.__dict__) # {'x': 123, 'y': 456, 'z': 789}a.__dict__['x'] = 0print(a.__dict__) # {'x': 0, 'y': 456, 'z': 789}print(a.__dict__['x']) # 0
class A:z = 789def __init__(self):self.x = 123self.y = 456def foo(self):print(self.x + self.y)def bar(self):return self.x / self.yA.bar = bar # 猴子补丁(monkey patch)print(A.__dict__)
运行结果:
{‘module’: ‘main’,
‘z’: 789,
‘init’: <function A.initat 0x0000012BA24DA4C8>,
‘foo’: <function A.foo at 0x0000012BA24DA168>,
‘dict’: <attribute ‘dict’ of ‘A’ objects>,
‘weakref’: <attribute ‘weakref’ of ‘A’ objects>, # 弱引用
‘doc’: None, ‘bar’: <function bar at 0x0000012BA24DA438>}
通过 hasattr()可以查看是否拥有属性。
class A:z = 789def __init__(self):self.x = 123self.y = 456a = A()print(hasattr(a, 'abc')) # Falseprint(hasattr(a, 'x')) # True
通过 setattr()可以设置属性。
class A:z = 789def __init__(self):self.x = 123self.y = 456a = A()setattr(a, 'x', 777)print(a.x) # 777setattr(a, 'h', 888)print(a.h) # 888
通过 getattr()可以获得属性。
class A:z = 789def __init__(self):self.x = 123self.y = 456a = A()print(getattr(a, 'x')) # 123print(getattr(a, 'r', 111)) # 没有可以设置默认值:111
通过 delattr()可以删除属性。
class A:z = 789def __init__(self):self.x = 123self.y = 456a = A()delattr(a, 'y')print(a.y) # 报错
__ setattr__、__ getattribute__、__ getattr__的使用
class User:z = [7,8,9]def __init__(self):self.money = 10000self.y = 'abc'def __setattr__(self, name, value):print('set %s = %s' % (name, value))object.__setattr__(self, name, value)def __getattribute__(self, name):print('getattribute %s' % name)return object.__getattribute__(self, name)def __getattr__(self, name):print('getattr %s' % name)return -1def foo(self, x, y):return x * y
当u = User()时,会执行__ init__()和__ setattr__()
当我们调用foo函数时u.foo(x,y),会执行__ getattribute__()
当我们找不到属性时,会执行__ getattribute__()和__getattr__()
12.槽(__ slots__)
其可以固定类所具有的属性,使实例不会分配__dict__,也无法动态添加属性,但其可以优化内存分配。
如果觉得《Python的面向对象(二):Python中神奇的魔法方法》对你有帮助,请点赞、收藏,并留下你的观点哦!