文章目录
双下方法
Python中有大量类似__init__这种以双下划线开头和结尾的双下方法,也可以叫做魔法方法,它们有着非常重要的地位和作用,也是Python语言独具特色的语法之一!
__str__ (实例对象被打印时执行)
此内置方法可以在实例对象被执行打印操作
(print、前端打印等等)时候自动返回一个数据,这个数据只能为字符串
的形式。通过这个内置方法可以更好的描述一个实例对象。
代码示例(不加__str__)
class MyClass(object):
pass
x = MyClass()
print(x)
若不加双下str方法打印的结果是: <__main__.MyClass object at 0x0000018B2F876B30>
代码示例(加__str__)
class MyClass(object):
def __str__(self):
return 'from __str__'
x = MyClass()
print(x)
加了双下str方法打印结果是: from __str__
需要注意的是,返回的数据必须是字符串格式,且要用return返回,只用print会报错。
__del__ (实例对象被删除时执行)
此内置方法在实例对象被删除的时候执行,这个删除可以是主动删除或者是被动删除。
代码示例(被动删除)
class MyClass(object):
def __del__(self):
print('from __del__')
x = MyClass()
print('xxx')
打印结果:
xxx
from __del__
虽然在代码中并没有进行删除操作,但是python有一个垃圾回收机制,也就是说,不用的会被回收删除,所
以执行了 __del__ 里面的代码。
代码示例(主动删除)
class MyClass(object):
def __del__(self):
print('from __del__')
x = MyClass()
del x
print('xxx')
打印结果
from __del__
xxx
此示例使用了del主动的将实例对象删除,此时和被动删除示例的打印结果不同的是 xxx 在最后了。也就
是说先执行了 __del__ 里的代码。
__getattr__ (实例对象查找不存在的属性时执行)
这个内置方法和前面文章中写的反射的方法 getattr 长得很相似。getattr 是用于获得类或对象中对应的变量名或者方法名没有返回None。而 __getattr__
方法是在实例对象查找不存在
的属性名时执行。
代码示例(不加__getattr__)
class MyClass(object):
pass
x = MyClass()
print(x.name)
由于在类和实例化对象名称空间中均没有name属性,此时运行代码会报错找不到name:
AttributeError: 'MyClass' object has no attribute 'name'
代码示例(加__getattr__)
class MyClass(object):
def __getattr__(self, item): # item 参数接收查找的属性名
print('from __getattr__')
return '没有找到属性时执行'
x = MyClass()
print(x.name)
打印结果
from __getattr__
没有找到属性时执行
此时没有找到name属性,执行__getattr__方法中的代码,也可以返回一个值给x.name。
__setattr__ (实例对象添加属性时执行)
此方法也和反射中的 setattr 类似,反射中的 setattr 是给类或者对象添加属性的,而内置方法__setattr__
是在实例对象添加属性时执行。
代码示例一
class MyClass(object):
def __setattr__(self, key, value):
print('from __setattr__')
x = MyClass()
x.name = 'xxx'
print(x.__dict__)
打印结果
from __setattr__
{}
1. 我们发现此时 __setattr__ 方法虽然在给实例化对象添加属性时执行了,但是在打印实例化对象的名称
空间的时候却没有发现该属性。
2. 因为我们此时相当于重写了 __setattr__ 方法,而原本凡是赋值操作都会触发它的运行,重写了就失去
了原有功能,我们可以使用 super 方法进行继承回来,或者使用名称字典的方式添加如下示例。
代码示例二
class MyClass(object):
def __setattr__(self, key, value):
print('from __setattr__')
# self.key = value # 会一直迭代死循环
self.__dict__[key] = value # 使用名称字典的方式添加属性
# super().__setattr__(key, value) # 或者重新继承object中的方法
x = MyClass()
x.name = 'xxx'
print(x.__dict__)
print(x.name)
打印结果
from __setattr__
{'name': 'xxx'}
xxx
1. 在示例一中我们知道他的属性并没有被加进去,我们可以在方法 __setattr__ 中使用 __dict__ 的方
式来给实例化对象添加属性。也可以重新继承 object 中的 __setattr__ 方法。
2. 如果使用了 self.key = value 来添加属性的话,又会执行 __setattr__ 方法形成了一个死循环。
3. 我们也可以在 __setattr__ 方法中添加一些条件判断,例如 key != 'name'、key.islower()...
__getattr__、__setattr__ 练习题
练习题要求是让字典具备句点符查找值和设置值的功能
字典是不能像类调用属性一样直接点的形式就可以获得值,所以我们要进行修改。
代码示例
class MyDict(dict):
def __getattr__(self, item):
return self.get(item)
def __setattr__(self, key, value):
self.__dict__[key] = value
super().__setattr__(key, value)
# 由于继承了 dict ,如果不加参数那么 x 就是一个空字典
x = MyDict({'name': 'XWenXiang', 'age': 12})
print(x.name) # 打印键名 name
x.name = 'x' # 修改键名name
print(x.name) # 打印修改后的键名name
print(x.school) # 打印不存在的键名,由于是 get() 方法,所以返回 None
打印结果
XWenXiang
x
None
1. 我们对字典功能进行修改,首先肯定要创建一个类继承字典。
2. 实例化类并传入一个字典,若是不传字典实例对象是一个空字典。
3. 我们可以先用点的方式来获取和修改值试一试,会报错,因为点的方式获取的属性并不存在,所以我们要
使用方法 __getattr__ 来执行属性不存在时的代码
4. 在由于实例对象是字典,所以在方法 __getattr__ 中用 get() 的方式来获取字典的值,并返回出去。
5. 此时就是实现了用点的方式获取字典里的值,同样的用点的方式修改字典的值用 __setattr__ 方法,
该方法在给实例对象赋值的时候执行。如果不用__setattr__ 方法虽然打印修改的结果变了,但是没有加到
字典里面
6. 在方法 __setattr__ 中用字典修改值的方式对实例对象修改即可。
__call__ (实例对象被加括号调用的时候执行)
我们知道实例对象是不能向函数名一样加括号调用的,调用就会报错。而__call__
就是在对象加括号调用的时候执行,就不会报错了。
代码示例
class MyClass(object):
def __call__(self, *args, **kwargs):
print('from __call__')
x = MyClass()
x() # 实例对象加括号调用
打印结果: from __call__
给实例对象加括号执行 __call__ 的代码。
__enter__ (实例对象执行 with 语句开始执行)
__exit__ (实例对象执行 with 语句结束执行)
这俩个内置方法在执行 with 上下文管理开始的时候执行,而且少一个就会报错。其中方法__enter__
可以返回一个值,这个值被 with 语法中 as 后面的变量接收,返回什么 as 后的变量就是什么。而方法__exit__
则是 with 语句结束的时候执行。
代码示例
class MyClass(object):
def __enter__(self): # with 语句开始时候执行
print('from __enter__')
return self # 返回一个值到 as 后面的变量(这里使用的是 self )
def __exit__(self, exc_type, exc_val, exc_tb): # with 结束的时候执行
print('from __exit__')
x = MyClass()
x.name = 'xxx' # 给实例对象添加一个属性 name
with x as f: # x 是实例对象,f 是方法 __enter__ 返回的值
print('这是实例对象: ', f) # 这里用 self 传给 f
print('这是实例对象中的属性: ', f.name)
打印结果
from __enter__
这是实例对象: <__main__.MyClass object at 0x000001A86A2454E0>
这是实例对象中的属性: xxx
from __exit__
__exit__、 __enter__ 的练习题
补全下面的代码使其不报错
class Context:
pass
with Context() as ctx:
ctx.do_something()
1. 首先我们可以看到这是一个 with 语句,with 后的跟着的也是类加括号也就是一个实例,但是此时并没
有方法 __exit__ 和 __enter__,所以先创建这俩方法
2. 然后发现在 with 中的 ctx 调用了一个方法,在类中除了实例对象可以调用方法还有的就是用类名来调
用,但是上面的调用方式并没有传入参数,也就是说是用实例对象调用的,所以在方法 __exit__ 中返回实
例对象。
3. 创建方法 do_something() 即可
代码示例
class Context:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self):
pass
with Context() as ctx:
ctx.do_something()
__getattribute__ (实例对象查找属性时执行)
这个代码和 __getattr__
方法有点像,__getattr__
是在查找不存在的属性时执行,而方法__getattribute__
只要实例对象查找名字无论名字是否存在都会执行该方法
'''如果类中有__getattribute__方法 那么就不会去执行__getattr__方法'''
代码示例
class MyClass(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('from __getattr__')
return '查找不存在的属性时执行'
def __getattribute__(self, item):
print('from __getattribute__')
return '查找属性时就会执行'
x = MyClass('xxx')
print(x.name) # 查找已存在的属性
print(x.age) # 查找不存在的属性
打印结果
from __getattribute__
查找属性时就会执行
from __getattribute__
查找属性时就会执行
在示例中,虽然有内置方法 __getattr__ ,也查找了不存在的属性,但是并没有执行方法 __getattr__,
而是只执行了方法 __getattribute__ 。