面向对象之反射和其他内置方法
一、反射
1、概念:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。简而言之,就是自身调用自身即可实现已定义的某一功能,以达到简化程序的作用。
2、python面向对象中的反射是指通过字符串的形式操作对象相关的属性。因为python中一切事物都是对象,所以都可以使用反射。一句话,就是通过字符串映射到对象的属性。
3、示例:
!/usr/bin/env python3 #-*- coding:utf-8 -*- # write by congcong # 反射:通过字符串映射到对象的属性 class People: def __init__(self,name,age): self.name = name self.age = age def study(self): # 绑定对象方法 print('%s is studying now'%self.name) p1 = People('cc',21) print(p1.name) # cc
# hasattr(obj,str) 判断字符串所对应的属性是否在指定对象中存在
print(hasattr(p1,'name')) # True
print(hasattr(p1,'study')) # True
print(hasattr(People,'study')) # True
print(hasattr(People,'name')) # False
# getattr(obj,str,None) 获取对象中字符串对应的属性,没有时返回None
print(getattr(p1,'name',None)) # cc
print(getattr(p1,'study',None)) # <bound method People.study of <__main__.People object at 0x0000024C49773320>>
print(getattr(People,'name',None)) # None
print(getattr(People,'study',None)) # <function People.study at 0x000001AEE4128B70>
# setattr(obj,str) # 设置属性,添加或修改属性
setattr(p1,'sex','male')
setattr(p1,'age',22)
setattr(People,'country','China')
print(p1.__dict__) # {'name': 'cc', 'age': 22, 'sex': 'male'}
print(People.__dict__)
# {'__module__': '__main__', '__init__': <function People.__init__ at 0x00000164440C8AE8>, 'study': <function People.study at 0x00000164440C8B70>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'country': 'China'} # delattr(obj,str) 删除属性
delattr(p1,'sex')
delattr(People,'country')
print(p1.__dict__) # {'name': 'cc', 'age': 22}
print(People.__dict__)
# {'__module__': '__main__', '__init__': <function People.__init__ at 0x0000021A437E8AE8>, 'study': <function People.study at 0x0000021A437E8B70>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
4、反射的好处
优点1:实现可插拔机制
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
优点2:动态导入模块(基于反射当前模块成员)
python提供了一个特殊的方法:__import__(字符串参数)。
通过它,我们就可以实现类似的反射功能。__import__()方法会根据参数,动态的导入同名的模块。
import commons
def run():
inp = input("请输入您想访问页面的url: ").strip()
modules, func = inp.split("/")
obj = __import__("lib."
+
modules, fromlist
=
True
) # 注意fromlist参数
if hasattr(obj, func):
func = getattr(obj, func)
func()
else:
print("404")
if __name__ == '__main__':
run()
请输入您想访问页面的url: commons/home
这是网站主页面!
请输入您想访问页面的url: account/find
这是一个查找功能页面!
5、反射的一个小例子
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 反射的应用
class Handle:
def run(self):
exit_flag = False
while not exit_flag:
user_in = input('>>:').strip()
hand_cmd = user_in.split()
if hasattr(self,hand_cmd[0]):
func = getattr(self,hand_cmd[0])
func(hand_cmd)
elif user_in=='q':
exit_flag=True
else:
print('error input')
def put(self,hand_cmd):
print('putting now',hand_cmd)
def get(self,hand_cmd):
print('getting now',hand_cmd)
f = Handle()
f.run()
二、其它内置方法
1、isinstance(object,cls) 和 issubclass(sub,super)
<1> isinstance(object,cls) 检查是否是类cls的对象
class Func(object):
pass
obj = Func()
print(isinstance(obj, Func)) # True
<2> issubclass(sub, super)检查sub类是否是 super 类的子类
class Foo(object):
pass
class Bar(Foo):
pass
print(issubclass(Bar, Foo)) # True
2、item系列
class Func():
def __init__(self,name):
self.name = name
def __getitem__(self, item):
print('from getitem')
return self.__dict__.get(item)
def __setitem__(self, key, value):
print('from setitem')
self.__dict__[key] = value
def __delitem__(self, key):
print('from delitem')
# print('del obj[key]时,执行')
del self.__dict__[key]
self.__dict__.pop(item)
p1 = Func('cc')
print(p1.__dict__) # {'name': 'cc'}
# 查看属性
print(p1['name']) # from getitem cc 触发__getitem__
print(p1.name) # cc
# 设置属性
# p1.sex = 'male' # 不会触发__setitem__
p1['sex'] = 'male' # from setitem 会触发__setitem__
p1['name'] = 'SC'
p1['age'] = 21
print(p1.__dict__) # {'name': 'SC', 'sex': 'male', 'age': 21}
# 删除属性
# del p1.sex # 不会触发__delitem__
del p1.age
del p1['sex'] # 触发__delitem__,输出:from delitem
print(p1.__dict__) # {'name': 'SC'}
注意:<1> item系列方法, 查看p1['name'] 、设置p1['name']、del p1['name'] 才会分别触发 __getitem__、__setitem__和__delitem__内置方法,执行对应方法。
<2>能写成p1['name']的分为两种情况:
1、字典
2、def __setitem__(self, key, value):pass
3、attr系列
# __setattr__,__delattr__,__getattr__ class Fun: 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 #无限递归 self.__dict__[key]=value # 正确姿势
def __delattr__(self, item): print("--->from delattr") # del self.item #无限递归 self.__dict__.pop(item) # __setattr__添加/修改属性会触发它的执行 f1 = Fun(66) print(f1.__dict__) # --->from setattr {'y': 66} 因为重写了__setattr__,凡是赋值操作都会触发它的执行 f1.z = 666 print(f1.__dict__) # --->from setattr {'y': 66, 'z': 666} # __getattr__只有在使用点调用属性且属性不存在时才会触发 print(f1.y) # 66 print(f1.xx) # ---> from getattr:你寻找的属性不存在 # __delattr__删除属性的时候会触发 f1.__dict__['aa'] = 6688
print(f1.__dict__) #{'y': 66, 'z': 666, 'aa': 6688}
del f1.aa # 触发__delattr__,输出: --->from delattr
print(f1.__dict__) # {'y': 66, 'z': 666}
del f1.__dict__['y'] #未触发__delattr__
print(f1.__dict__) # {'z': 666}
注意:对于 attr 系列内置方法,只有在使用点调用属性且属性不存在时才会触发__getattr__方法;
使用点方法添加/修改属性会触发__setattr__方法的执行;使用点方法删除属性会触发__delattr__方法。
4、__str__类定制
class Str:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return '<name:%s age: %s>'%(self.name,self.age) # 必须有返回值
msg = Str('hello',999)
print(msg) # <name:hello age: 999> 打印对象时触发__str__方法,实现定制化输出
5、__init__和__del__
# 回收操作系统的资源
class Open:
def __init__(self,filename):
print('open file now')
self.filename = filename
def __del__(self): # 相当于self.close()等关闭操作,在此方法中进行Python不能自动回收的资源回收操作
print('回收系统资源')
f = Open('settings.py') # 包含两部分操作,一是Python解释器开辟一块内存并赋给f,二是在系统硬盘层面打开了一个文件
print('End...') # 触发del f --> f.__del__(Python程序结束会自动回收程序中的变量,对象,但不能关闭系统资源)
'''
open file now
End...
回收系统资源
'''