Python 5 面向对象进阶
一、反射
反射就是用字符串类型的变量名来访问这个变量。
hasattr,getattr,setattr,delattr
二、内置方法(魔术方法)
1、__new__方法(构造方法)
在实例化对象时,第一步就是为对象开辟一块新的内存空间,然后将这个空间传给init方法。开辟空间就是new方法做的事。
# 示例:利用new方法构造一个单例模式 class Single: __ISINSTANCE = None def __new__(cls, *args, **kwargs): if not cls.__ISINSTANCE: # 第一次实例化 cls.__ISINSTANCE == object.__new__() return cls.__ISINSTANCE def __init__(self): pass
2、__del__析构方法
在两种情况下生效,一是使用del,二是python的垃圾回收机制生效时触发。一般析构方法用来释放一些在创建对象时借用的系统资源,如文件资源、网络资源等,这些资源是操作系统层面的,python不能直接控制,所以python的垃圾回收也不会顾及这些,需要析构方法来帮助回收。
class File: def __init__(self, file_path): self.f = open(file_path) def __del__(self): self.f.close() # 当程序执行完后会自动执行。
3、__call__方法,通过对象( )调用
4、__len__方法,len( )函数实际上就是在执行__len__方法
5、__next__,__iter__
6、__str__和__repr__
__repr__会把字符串的两个引号也显示出来。当str和repr两个方法都有时,优先使用str方法,当str方法没有时,str()和print都会使用repr方法。
7、__hash__
hash(obj)方法中的obj对象必须定义了__hash__方法。
hash是一种算法,能把一个值经过一系列计算,转化成一个数字,不同的值转化的数字不同,在一次操作中,相同的值转化成相同的数字。
字典的寻址和集合的去重就是利用了hash算法。
字典寻址是先将字典的键保存在任意位置,再hash这个键,得到的数字就是值的内存地址,所以当需要找一个键对应的值的时候,直接hash这个键就能得到内存地址,速度很快。
集合在保存数据时对每个值进行hash,得到内存地址,将这个值保存在这里,如果后面的值hash得到了相同的结果,再判断两个值是否相等,如果想等就去重,所以集合保存的就是每个值的内存地址,没有重复的。
8、__eq__
==和__eq__方法有关,当进行==比较时就是在执行eq方法,可以定义两个对象相等的条件。
class Student: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if self.name == other.name and self.age == other.age: return True # 如果两个对象的name和age都相等就认为这两个对象相等 a1 = Student("a", 1) a2 = Student("a", 1) print(a1==a2) # 得到True
三、抽象类和接口类
都是用来规范子类中必须实现的方法。无论如何这两种都不能被实例化。
python中有原生实现抽象类的方法,但是没有原生实现接口类的方法。
在java中只支持单继承,遵循单继承原则的基类就是抽象类,抽象类中的方法都可以实现。但是java不支持多继承,如果想实现多继承需要使用interface创建接口类,接口类中的方法不能被实现智能pass,接口类都是用来被子类多个继承的。
from abc import ABC, abstractmethod class Te(ABC): @abstractmethod def test(self): pass
四、其他
1、__slots__类属性
如果定义了__slots__类属性,那么这个类在实例化时,就会选择用和__slots__同类型的可迭代对象(如元组)来保存实例变量,而不是使用需要占用很多内存的字典__dict__属性。这样可以节约很多内存,尤其是在实例非常多的时候,避免无谓的内存占用。
注意:__slots__属性不会被子类继承,所以子类必须重新定义。
class Test: __slots__ = ("m", "k") # 只能有m和k两个实例变量