面向对象的反射和一些魔法方法
- 类相关的内置函数
# 类相关的内置函数 # 1.isinstance 用于判断,对象是否是对应类型,返回True或者False 接收两个参数,(实例化的对象,类型) 如果存在继承关系也会返回True isinstance(1,int) # 2.issubclass 用于判断类于类之间的继承关系 例:issubclass(B,A)判断B是否继承与A 参数只能传入类 class A:pass class B(A):pass a = A() b = B() issubclass(A,A) # == 跟 is 的区别 (== 判断值是否相等,is判断内存地址是否相同)
- 反射
# 反射 # 用字符串数据类型的变量名来访问,这个变量的值 ''' getattr: 使用字符串来访问变量,或者执行方法 # 访问变量 getattr(对象|类|模块,'变量名') # 执行方法 getattr(对象|类|模块,'方法名')(参数) hasattr: 用于判断,该属性或者方法在该对象中是否存在 hasattr(对象|类|模块,'方法名|属性名') setattr: 用于修改对象属性 setattr(对象|类|模块,'属性名','新值') delattr: 用于删除对象属性 delattr(对象|类|模块,'属性名') ''' class A: name = '777' def func(self,arg): print(arg) a = A() import sys print(sys.modules.get('__main__'))
- __call__方法
# __call__ 实例化对象加() 就会调用 __call__ 方法 class A: def __call__(self,*args,**kwargs): print('args:',args) print('kwargs:',kwargs) print('A 的__call__方法被调用了') # 第一种使用方法 # a = A() # a() class B: def __init__(self,cls): self.a = cls() self.a() # 第二种使用方法 # b = B(A)
- __len__方法
# __len__ 在执行内置函数len()的时候回调用这个方法,该方法必须返回一个整数类型,否者会报错 # __len__ return 的值就是len()函数计算出的长度 class MyObj: def __init__(self,*args): self.args =args def __len__(self): print('MyObj的__len__方法被调用了') return 1 obj = MyObj() print(len(obj)) # 练习 class MyStr: def __init__(self,args): self.string = args def __len__(self): return len(self.string) s = MyStr('asc') print(len(s))
- __new__方法
# __new__ 构造方法,在类名+()的时候调用,然后会开辟一块内存。返回给__init__(初始化方法)中的self class A: def __new__(cls,*args,**kwargs): # 由于Python语言无法开辟空间,我们得调用父类(object)的__new__方法来开辟空间(下面两种方法都可以) # obj = object.__new__(cls) obj = super().__new__(cls) print(obj) return obj def __init__(self): print('在初始化方法中',self) A() # 使用场景 单例类 class B: __ISINCTANCE = None def __new__(cls,*args,**kwargs): if not cls.__ISINCTANCE: cls.__ISINCTANCE = super().__new__(cls) return cls.__ISINCTANCE def __init__(self,name): print(self) self.name = name a = B(1) b = B(2) c = B(3) print(a,b,c) print(a.name,b.name,c.name)
- __str__方法
# __str__ 在执行print|str|'%s'% obj 的时候 调用 class A: def __init__(self,name): self.name = name def __str__(self): return self.name a = A('444') print(a) print(str(a)) print('字符串格式化:%s'%a)
- __repr__方法
# __repr__ # 内置函数 repr() 原样输出,在打印输出的时候,原样显示\n \t等特殊字符,输出的字符串也会被""包括(但是%s占位符还是可以正常使用) # 如果没有__str__,那么调用 __str__方法的操作会找__repr__方法来取值 class A: def __init__(self,name): self.name = name # def __str__(self): # return self.name def __repr__(self): return '\'%s\''%self.name class B(A): def __repr__(self): return '\'%s\''%self.name a = B('aaa') print(a) print(repr(a))
- __del__ 方法
# __del__ 析构方法 释放一个空间 # 跟构造方法相反 __new__ 构造方法 申请一个空间 class A: def __del__(self,*args,**kwargs): ''' 执行 del key 的时候触发 就算这里没有写 del相关语法,照样可以删除元素 ''' print('被删除了...') a = A() import time time.sleep(10)
- item系列
# item 系列 # __getitem__ 、__setitem__ class B: def __getitem__(self,item): ''' b = B() 在执行 b['key'] 的时候调用此方法 item 会接收到key ''' return getattr(self,item) def __setitem__(self,key,value): ''' b = B() 在执行 b['key']=123 的时候调用此方法 key 会接收到key ,value会接收到123 ''' setattr(self,key,value) def __delitem__(self,key): ''' b = B() 在执行 del b['key']的时候调用此方法 key 会接收到key ''' delattr(self,key) b = B() b['a123'] = 666 b['a123'] del b['a123'] b['a123']
- __hash__,__eq__方法
# 面试题 __hash__、__eq__ ''' 员工管理系统类 属性 姓名,性别,年龄,部门 存在重复员工信息,但是部门不同(如果存在姓名相同,性别相同。我就认为这是同一个人) 有1000的员工对象,筛选出重复的员工 ''' class Staff: def __init__(self,name,sex,age,department): self.name = name self.sex = sex self.age = age self.department = department def __hash__(self): # 自定义类的hash条件 return hash('%s%s'%(self.name,self.age)) def __eq__(self,other): # 使用 == 语法的时候,会触发这个方法,用于判断是否相等 # 如果hash值相同,会触发这个方法,来判断值是否相同(True相同,False不同) if self.name == other.name and self.sex == other.sex: return True s1 = Staff('张三','男',28,'运营') s2 = Staff('李四','男',28,'人事') s3 = Staff('王五','男',28,'财务') s4 = Staff('钱六','男',78,'销售') s5 = Staff('王五','男',28,'销售') staff_list =[s1,s2,s3,s4,s5] ''' # 使用集合的去重机制 1.计算hash值(调用对象的__hash__方法) 1.1 如果hash值不同,则存入hash对应的内存地址 1.2 如果hash相同:判断值是否相同(调用对象的__eq__方法) 1.2.1 如果值相同 :则存入hash对应的内存地址,替换之前的值 1.2.3 如果值不同 :使用另外hash算法,进行二次寻址 最后留下来的就是不相同的 ''' staff_set = set(staff_list) print(staff_set)
- __setattr__、__getattr__、__delattr__
# 1 __ setattr__的使用 # 2 递归现象的出现 -通过反射赋值 setattr(obj,key,value) -self.key=value # 3 避免递归 -self.__dict__[key] = value -object.__setattr__(self,key,value) # 4 object.__setattr__(f,'_Foo__size',178)的使用 # 5 __getattr__ -对象.属性 取值属性不存在就会触发它的执行 # 6 __delattr__ - 删除对象.属性的时候会触发执行 # 7 应用场景(字典只支持中括号取值和赋值,让它支持 . 取值和赋值)
- __slots__
class A: __slots__ = ['name', 'age'] # 用于控制A类的对象只能点出那些属性 a1 = A() # print(a1.__dict__) # AttributeError: 'A' object has no attribute '__dict__' a1.name = '张三' a1.age = 24 print(a1.age) # a1.hobby = '泡妞' # AttributeError: 'A' object has no attribute 'hobby' a1.size=179 print(a1.__slots__)
- __doc__
class A: """我是A类的描述信息啊手动阀手动阀""" pass # 查看类的注释 print(A.__doc__)
- __enter__、__exit__
class B: def __enter__(self): # 在使用with 对象 as 变量名: # 语句的时候 会将这个魔法方法的返回值 赋值给变量名 return self def __exit__(self, exc_type, exc_val, exc_tb): """ 当with代码块执行结束之后,会执行这个魔法方法中的代码 如果with是正常结束的,那么三个参数都是None 如果with遇到了异常 :param exc_type: 异常类型 :param exc_val: 异常提示信息 :param exc_tb: traceback类,异常的一些信息,哪一行报的错误等 :return: """ print('with结束了')