魔法方法
Python里面有一种特殊的方法,叫做魔法方法。Python的类里提供的,是由两个下划线开始,再以两个下划线结束的方法。
魔法方法不需要手动调用,会在恰当的时候就会被激活,自动执行。
魔法方法的名字都是系统规定好的,不能乱写,在合适的时候自己调用。
1.__init__方法
_init_()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__ 方法进行改造。
class Student(object):
def __init__(self, name): # 重写了___init__魔法方法,设置对象的属性
# 在创建对象时,会自动调用这个方法。
print('__init__方法被调用了')
self.name = name
def 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 = name
def __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 = name
s1 = Student('张三')
print(s1) # <__main__.Student object at 0x00000298D1161808>
那我们如何得到s1的值呢?其实想要修改对象的输出结果,我们就需要重写__str__方法。
class Student(object):
def __init__(self, name):
self.name = name
def __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 = name
def __repr__(self):
return '姓名:{}'.format(self.name)
s1 = Student('张三')
print(s1) # 姓名:张三
倘若__repr__方法和__str__方法同时出现,会发生什么情况呢???
class Student(object):
def __init__(self, name):
self.name = name
def __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 = name
def __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 = name
def __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 = name
self.score = score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)
s1和s2是否是同一个对象?
比较两个对象是否是同一个对象,我们需要比较的是内存地址
print('0x%X' % id(s1)) # 0x16AA83BB408
print('0x%X' % id(s2)) # 0x16AA83BB448
由上可知,它们的内存地址不一样,两个对象不是同一个对象。
print('s1 is s2', s1 is s2) # s1 is s2 False
print(s1 == s2) # False
is 身份运算符,比较两个对象的内存地址
== 会调用对象的 eq 方法,如果不重写,默认比较依然是内存地址,如果重写方法,便可以获取这个方法的比较结果。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
return self.name == other.name and self.score == other.score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)
print( s1 == s2) # True
其它比较运算符,也是如此。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __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.score
def __gt__(self, other):
return self.score > other.score
def __le__(self, other):
return self.score <= other.score
def __ge__(self, other):
return self.score >= other.score
s1 = Student('zhangsan', 90)
s2 = Student('zhangsan', 90)
s3 = Student('lisi', 94)
s4 = Student('wangwu', 99)
print(s1 == s2) #True
print(s1 != s2) #False
print(s1 > s3) #False
print(s1 >= s4) #False
print(s1 < s4) #True
print(s1 <= s3) #True
7.算数运算符相关的魔法方法
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __add__(self, other):
return self.score + other
def __sub__(self, other):
return self.score - other
def __mul__(self, other):
return self.name * other
def __truediv__(self, other):
return self.score / other
def __mod__(self, other):
return self.score % other
def __pow__(self, power, modulo=None):
return self.score ** power
s = Student('zhangsan', 95)
print(s + 1) # 96
print(s - 2) # 93
print(s * 2) # zhangsanzhangsan
print(s / 5) # 19.0
print(s % 5) # 0
print(s ** 2) # 9025
8.类型转换相关的魔法方法
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __int__(self):
return self.score
def __float__(self):
return self.score * 1.0
def __str__(self):
return self.name
def __bool__(self):
return self.score > 60
s1 = Student('zhangsan', 90)
print(int(s1)) # 90
print(float(s1)) # 90.0
print(str(s1)) # zhangsan
print(bool(s1)) # True
9.容器方法
__ len__ -> len 计算长度
__ iter__ -> for for循环
__ 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 self
def __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 = 123
self.y = 456
a = A()
a.z = 789
print(a.__dict__) # {'x': 123, 'y': 456, 'z': 789}
a.__dict__['x'] = 0
print(a.__dict__) # {'x': 0, 'y': 456, 'z': 789}
print(a.__dict__['x']) # 0
class A:
z = 789
def __init__(self):
self.x = 123
self.y = 456
def foo(self):
print(self.x + self.y)
def bar(self):
return self.x / self.y
A.bar = bar # 猴子补丁(monkey patch)
print(A.__dict__)
运行结果:
{‘module’: ‘main’,
‘z’: 789,
‘init’: <function A.init at 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 = 789
def __init__(self):
self.x = 123
self.y = 456
a = A()
print(hasattr(a, 'abc')) # False
print(hasattr(a, 'x')) # True
通过 setattr()可以设置属性。
class A:
z = 789
def __init__(self):
self.x = 123
self.y = 456
a = A()
setattr(a, 'x', 777)
print(a.x) # 777
setattr(a, 'h', 888)
print(a.h) # 888
通过 getattr()可以获得属性。
class A:
z = 789
def __init__(self):
self.x = 123
self.y = 456
a = A()
print(getattr(a, 'x')) # 123
print(getattr(a, 'r', 111)) # 没有可以设置默认值:111
通过 delattr()可以删除属性。
class A:
z = 789
def __init__(self):
self.x = 123
self.y = 456
a = A()
delattr(a, 'y')
print(a.y) # 报错
__ setattr__、__ getattribute__、__ getattr__的使用
class User:
z = [7,8,9]
def __init__(self):
self.money = 10000
self.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 -1
def foo(self, x, y):
return x * y
当u = User()时,会执行__ init__()和__ setattr__()
当我们调用foo函数时u.foo(x,y),会执行__ getattribute__()
当我们找不到属性时,会执行__ getattribute__()和__getattr__()
12.槽(__ slots__)
其可以固定类所具有的属性,使实例不会分配__dict__,也无法动态添加属性,但其可以优化内存分配。