OOP 内置函数
内置函数:类中存在一些名字带有__
(双下滑线)开头的内置函数,这些函数会在某些时候被自动调用
1. isinstance & issubclass
isinstance:判断一个对象是否是某个类的实例
用法:isinstance(obj, cls) 检查 obj 对象是否是 cls 类 (不仅限于类中)
issubclass:判断一个类是否是另一个类的子类
用法:issubclass(sub, super) 检查 sub 类是否是 super 类的派生类
2. __str__
用法:调用 str 函数或者 print 函数时(输出结果为字符串)自动执行,返回值作为显示内容
使用场景:我们可以利用该函数来自定义对象的打印格式(不设置则默认解释器里面内置的)
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return '我叫%s,我今年%s岁了' % (self.name, self.age) # 可以自定义输出格式 p = Person('zhao', 24) print(p) # 我叫zhao,我今年24岁了
3. __del__
当对象在内存中被释放时(手动删除对象、程序运行结束时),会自动触发执行
使用场景:当你的对象在使用过程中打开了不属于解释器的资源:例如文件、网络端口
class File: def __del__(self): print('已删除...') f = File() del f # 已删除...
4. __call__
在对象被调用时执行
用法:对象()
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()的区别
class Person: def __init__(self,name,age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('执行我了') print(*args) print(**kwargs) p = Person('zhao', 24) # 执行 __init__ p() # 执行我了 执行 __call__ p('hobby = read') # 执行我了 hobby = read 执行 __call__
5. __slots__
该属性是一个类属性,用于优化对象内存占用
优化的原理:将原本不固定的属性数量,变得固定了
这样的解释器就不会为这个对象创建名称空间,所以__dict__也没了
从而达到减少内存开销的效果
用法:
class Person: __slots__ = ['name'] p = Person() p.name = 'jake' print(p.__slots__) # ['name'] p.age = 18 # 报错,当类中出现了slots时将导致这个类的对象无法在添加新的属性
''' Traceback (most recent call last): File "D:/代码练习/7.29/clots.py", line 10, in <module> p.age = 18 AttributeError: 'Person' object has no attribute 'age '''' print(p.__dict__) # 报错,使用slots后就不存在__dict__ ''' AttributeError: 'Person' object has no attribute 'age '''' ^ SyntaxError: EOL while scanning string literal '''
6. __getattribute__ & __getattr__ & __setattr__ & __delattr__
__getattribute__:使用 点语法 调用 属性的时候触发,无论属性是否存在都会执行
__getattr__:使用 点语法 调用 属性且属性不存在的时候才会触发
__setattr__:使用 点语法 添加/修改 属性会触发它的执行
__delattr__:使用 点语法 删除 属性的时候会触发
注意:当 __getattribute__ 与 __getattr__ 同时存在时,仅执行 __getattribute__
class Person: def __init__(self,name): self.name = name def __getattr__(self, item): return self.__dict__[item] def __setattr__(self, key, value): self.__dict__[key] = value def __delattr__(self, item): del self.__dict__[item] p = Person('zhao') print(p.__dict__) # {'name': 'zhao'} 此时只有这么一个元素 p.age = 24 print(p.__dict__) # {'name': 'zhao', 'age': 24} 触发了__setattr__,成功添加进去 print(p.age) # 24 触发了__getattr__,成功访问到新添加进去的值 del p.age # 触发__delattr__,成功删除p.age属性 print(p.__dict__) # {'name': 'zhao'}
7. __getitem__ & __setitem__ & __delitem__
__getitem__:使用 key 的形式获取属性时触发
__setitem__:使用 key 的形式添加/修改属性时触发
__delitem__:使用 key 的形式删除属性时触发
class Person: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self, item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): self.__dict__.pop(key) p = Person('zhao', 24) print(p.__dict__) # {'name': 'zhao', 'age': 24} p['name'] # 触发了__getitem__方法 print(p['name']) # zhao p['age'] = 18 # 触发了__setitem__方法 print(p['age']) # 18 del p['name'] # 触发__delitem__方法 print(p.__dict__) # {'age': 18}
8. __gt__ & __lt__ & __eq__ (运算符重载)
当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数
当我们需要自定义对象的比较规则时,就可在子类中覆盖大于、等于、小于等一系列方法....
class Person: def __init__(self,name,age): self.name = name self.age = age def __gt__(self, other): # 自定义比较的方法,在这里比较的是对象中的年龄,覆盖了原来的 other指的是另一个参与比较的对象 return self.age > other.age def __lt__(self, other): # 自定义比较的方法,在这里比较的是对象中的年龄,覆盖了原来的 other指的是另一个参与比较的对象 return self.age < other.age def __eq__(self, other): # 自定义比较的方法,在这里比较的是对象中的年龄,覆盖了原来的 other指的是另一个参与比较的对象 if self.age == other.age: return True return False p = Person('赵', 24) p1 = Person('张', 18) p2 = Person('王', 18) print(p > p1) # True print(p < p1) # False print(p1 == p2) # True
9. __iter__ & __next__ (迭代器协议)
回顾:迭代器是指具有__iter__和__next__的对象
因此:我们可以为对象增加这两个方法来让对象变成一个迭代器
''' 定义自己的range方法 ''' class MyRange: # 定义自己的range def __init__(self,start,end,step): self.start = start # 起始 self.end = end # 结束 self.step = step # 步长 def __iter__(self): return self # 迭代器对象调用__iter__方法后还是迭代器,生成器对象调用__iter__方法后生成迭代器对象 def __next__(self): a = self.start self.start += self.step if a < self.end: return a else: raise StopIteration # 不知道这是啥,也没讲,也不敢问 myrange = MyRange(1,6,2) for i in myrange: print(i) # 1 3 5
10. __enter__ & __exit__ (上下文管理)
上下文:context 在这个代码中python解释器分析出你的代码想要做的事情,然后在结束的时候自动帮你将资源释放了
with中的所有代码都在一个上下文中,你可以把他理解为一个代码范围---------仅在上下文范围内有效
__enter__:出现 with语句,对象的 __enter__
被触发,有返回值则赋值给 as 声明的变量
__exit__: with 中代码块执行完毕时执行
只要一个类实现了这两个方法就可以被 with 语句使用
class MyOpen: # 定义自己的打开
def __init__(self, filepath, mode='r', encoding='utf-8'):
self.filepath = filepath
self.mode = mode
self.encoding = encoding
def __enter__(self):
self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb): # 三个参数分别代表异常类型,异常值和追溯信息
self.f.close()
print('文件关闭了')
# return True
with MyOpen('xxx.txt', 'a') as f:
print(f) # <_io.TextIOWrapper name='xxx.txt' mode='a' encoding='utf-8'> 这是啥.....
f.write('你好啊!') # 结果成功写入
f.wasdf #抛出异常,交给__exit__处理
# 反正最后自己定义的MyOpen成功了,与内置的open具有相同的效果
注:1. with 语句中代码块出现异常时,会立即触发方法 __exit__ 的执行,并将异常信息错误参数传入
2. with 语句中代码块未出现异常正常结束时也会触发方法 __exit__ 的执行,此时参数中的异常信息为空
3. 如果 __exit__ 返回值为 True ,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
总结:
使用 with 语句的目的就是把代码块放入 with 中执行,with 结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在 __exit__ 中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
*******与del的区别:del管理的是对象的生命周期 ,会在对象销毁时执行清理