08-07 内置方法
一. 什么是内置方法?
"""
什么是内置方法?
定义在类内部, 以__开头 并以__结尾的方法
特点: 会在某种情况下自动触发执行
为什么要用内置方法?
为了高度定制化我们的类 或者 对象.
"""
二. __str__
"""
在打印对象时自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出
作用: 一般基于该方法来定制对象的打印信息,
"""
class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def __str__(self):
return f'姓名:{self.name} 年龄:{self.age}岁 性别:{self.sex}'
obj = People('egon', 18, '男')
print(obj)
三. __del__
'''
会在对象被清理之前, 会自动触发(提示: 程序运行完毕, 对象就会被GC回收, 这种清理之前, 会自动触发__del__方法的运行)
作用: 一般基于该方法来回收操作系统资源的操作.
由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
'''
class Open:
def __init__(self, my_mode, file='a.txt'):
self.f = open(file, mode=my_mode) # open()这里占据的是操作系统资源
def __del__(self):
print("正在回收f句柄对象所占用的系统资源.......")
self.f.close() # 发起系统调用,告诉操作系统回收相关的系统资源
print("f句柄对象所占用的系统资源回收完毕.......")
Open('wb')
四. isinstance(obj,cls) 和 issubclass(sub,super)
# isinstance(obj,cls)检查是否obj是否是类 cls 的对象
print(isinstance(1, str)) # False
print(isinstance('1', str)) # True
# issubclass(sub, super)检查sub类是否是 super 类的派生类
print(isinstance(type, object)) # True
print(issubclass(int, dict)) # False
五. __setattr__, __getattr__, __delattr__
'''
注意: 以下的自动触发的前提是操作对象达到某一条件, 自动触发其类中定义的方法.
同理, 类也是对象. 如果实例化类的元类中定义了以下的方法, 那么操作类达到某一条件, 也会自动触发其元类中定义的以下的方法.
__setattr__: 在添加或者修改属性的时候会自动触发.
__getattr__: 在`对象.属性`不存在时会自动触发
__delattr__: 在删除属性的时候会自动触发
'''
class Foo:
x = 1
def __init__(self, y):
self.y = y
def __getattr__(self, item):
print('----> from getattr:你找的属性不存在')
def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value # 这就无限递归了, 因为这里是对象的添加/修改操作, 又会触发__setattr__方法了.
# self.__dict__[key]=value # 应该使用它
def __delattr__(self, item):
print('----> from delattr')
# del self.item # 无限递归了
self.__dict__.pop(item) # 应该使用它
# __setattr__: 添加/修改属性会触发它的执行
f1 = Foo(10)
print(f1.__dict__) # {} 为什么这里是空的? 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z = 3
print(f1.__dict__) # {}
# __delattr__: 删除属性的时候会触发
f1.__dict__['a'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__) # {}
# __getattr__: 只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx # 会触发. 因为xxxxxx属性不存在
print(f1.x) # 不会触发
拓展: 继承dict类, 实现字典类型可以使用'对象.属性'操作获取值 以及 通过 '对象.属性=值'设置值
class MyDict(dict):
def __setattr__(self, key, value):
print("对象加点赋值,会触发我")
self[key] = value
def __getattr__(self, item):
print("对象加点取值,会触发我")
return self[item] # 不要加引号
my_dict = MyDict(name='lqz', age=18)
print(my_dict['name']) # 对象加点取值,会触发我
print(my_dict.name)
'''
lqz
对象加点赋值,会触发我
'''
my_dict.name = 99 # 对象加点赋值,会触发我
print(my_dict.name)
'''
对象加点取值,会触发我
99
'''
六. __getattribute__
1. __getattr__
class Foo:
def __init__(self, x):
self.x = x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
f1 = Foo(10)
print(f1.x) # 10
f1.xxxxxx # 不存在的属性访问,触发__getattr__
2. __getattribute__
class Foo:
def __init__(self, x):
self.x = x
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
f1 = Foo(10)
f1.x
f1.xxxxxx
3. __getattr__
和 __getattribute__
当__getattribute__与__getattr__同时存在,只会执行__getattribute__,除非__getattribute__在执行过程中抛出异常AttributeError.
class Foo:
def __init__(self, x):
self.x = x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
raise AttributeError('哈哈')
f1 = Foo(10)
f1.x
print('======>')
f1.xxxxxx
'''
不管是否存在,我都会执行
执行的是我
======>
不管是否存在,我都会执行
执行的是我
'''
七. __setitem__,__getitem,__delitem__
class Foo:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
# print(self.__dict__[item]) # 方式一
print(getattr(self, item)) # 方式二
def __setitem__(self, key, value):
print("obj[key]=value时,我执行")
# self.__dict__[key] = value # 方式一
setattr(self, key, value) # 方式二
def __delitem__(self, key):
print('del obj[key]时,我执行')
# self.__dict__.pop(key) # 方式一
delattr(self, key) # 方式二
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
# delattr(self, item) # 注意: 这里如果这样使用就递归了
f1 = Foo('sb')
# 自动触发__setitem__
f1['age'] = 18 # obj[key]=value时,我执行
f1['age1'] = 19 # obj[key]=value时,我执行
# 自动触发__delattr__
del f1.age1 # del obj.key时,我执行
# 先自动触发__delitem__, 再触发__delattr__
del f1['age']
'''
del obj[key]时,我执行
del obj.key时,我执行
'''
# 自动触发__setitem__
f1['name'] = 'alex' # obj[key]=value时,我执行
print(f1.__dict__) # {'name': 'alex'}
八. __doc__
class Foo:
"""我是描述信息"""
pass
print(Foo.__doc__) # 我是描述信息
该属性无法继承给子类
class Foo:
"""我是描述信息"""
pass
class Bar(Foo):
pass
# 该属性无法继承给子类
print(Bar.__doc__) # None
九. __module__和__class__
__module__
表示当前操作的对象在那个模块
__class__
表示当前操作的对象的类是什么
class C:
def __init__(self):
self.name = 'SB'
c = C()
print(c.__module__) # 输出 lib.aa, 即:输出模块
print(c.__class__) # 输出 lib.aa.C,即:输出类
from lib.aa import C
c = C()
print(c.__module__) # 输出 lib.aa, 即:输出模块
print(c.__class__) # 输出 lib.aa.C,即:输出类
十. __enter__和__exit__
1. 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__
和__exit__
方法
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
with Open('a.txt') as f:
print('=====>执行代码块')
# print(f,f.name)
2. __exit__()
中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print("exc_type:", exc_type)
print('exc_val:', exc_val)
print('exc_tb:', exc_tb)
with Open('a.txt') as f: # self.name='a.txt'
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print('0' * 100) # ------------------------------->不会执行
# 执行结果
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
exc_type: <class 'AttributeError'>
exc_val: ***着火啦,救火啊***
exc_tb: <traceback object at 0x000001E61C2DA908>
'''
3. 如果__exit__()
返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print("exc_type:", exc_type)
print('exc_val:', exc_val)
print('exc_tb:', exc_tb)
return True
with Open('a.txt') as f: # self.name='a.txt'
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print('0' * 100) # ------------------------------->会执行
# 执行结果
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
exc_type: <class 'AttributeError'>
exc_val: ***着火啦,救火啊***
exc_tb: <traceback object at 0x000001E61C2DA908>
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
'''
十一. __eq__, __lt__, __gt__
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, obj):
# 打印出比较的第二个对象的x值
print(obj.x)
if self.x + self.y == obj.x + obj.y:
return True
else:
return False
a = A(1, 2)
b = A(99, 3)
# 当执行==s时,会触发__eq__的执行,并且把b传进去,就是object
# ==后只要是对象,就可以传进去,就是object
print(a == b) # False
十二. 总结
__new__: 类实例化时自动触发. 在__init__之前. (例子: 造出裸体的人)
__init__: 类实例化后自动触发, 在__new__之后. (例子: 穿衣服)
__call__: 调用对象自动触发其类 或者 父类中定义的__call__方法
__str__: 打印对象时自动触发
__del__: 对象被回收时自动触发
__setattr__: 添加/修改属性时自动触发
__getattr__: `对象.属性`不存在时自动触发
__delattr__: 删除属性时自动触发
__getattribute__: `对象.属性`存在或者不存在都触发.
如果和__getattr__同时存在, 只会执行__getattribute__.
如果__getattribute__方法在执行过程中遇见异常, 会跑到__getattr__方法中执行. 如果__getattr__方法没有定义, 就只能抛出异常
__getitem__: `对象['属性']` 时自动触发
__setitem__: `对象['属性']=值` 时自动触发
__delitem__: `del ['属性']` 时自动触发
__enter__: with操作时自动触发, 返回值赋值给as后面的句柄对象
__exit__: with子代码块执行完毕以后自动触发.
三个参数: exc_type, exc_val, exc_db
第一个参数: 异常类型
第二个参数: 异常值
第三个参数: 异常追随信息
如果返回True则在with语句执行的过程中抛出异常不会影响with之后的语句的正常执行
__eq__: ==比较时自动触发. 等于号右边对象当作参数传入.
__lt__, __gt__
__class__: 获取当前类
__module__: 获取当前模块
__doc__: 获取文档描述. (提示: 子类继承, 无法获取)
十三. 拓展: 什么是构造方法? 什么是析构方法?
__init__: 构造方法
__del__: 析构方法
十四. 更多
https://www.cnblogs.com/linhaifeng/articles/6204014.html
https://www.cnblogs.com/liuqingzheng/articles/9949568.html#_label3