目录
反射
优点
实战案例
案例
使用内置函数改造
反射内建函数注意事项:实例方法绑定和非绑定的区别
动态增加属性方法的区别
反射
其实它的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!>>>>>通过字符串来操作对象的数据或方法.
python的四个重要内置函数:getattr
、hasattr
、delattr
和setattr
较为全面的实现了基于字符串的反射机制。他们都是对内存内的模块进行操作,并不会对源文件进行修改。
hasattr():判断对象是否含有某个字符串对应的属性
getattr():获取对象字符串对应的属性
setattr():根据字符串给对象设置属性
delattr():根据字符串给对象删除属性
import sys
class Commons:
@staticmethod
def login():
print("登录页面")
@staticmethod
def logout():
print("退出页面")
@staticmethod
def home():
print("这是网站主页")
this_moudle = sys.modules[__name__]
def run():
inp = input("请输入想访问页面的URl:").strip()
if hasattr(Commons,inp):
func = getattr(Commons,inp,'没有这个页面')
func()
else:
print("404!")
run()
class FtpServer:
def serve_forever(self):
while True:
inp = input('input your cmd>>: ').strip()
cmd, file = inp.split()
if hasattr(self, cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
func = getattr(self, cmd) # 根据字符串cmd,获取对象self对应的方法属性
func(file)
def get(self, file):
print('Downloading %s...' % file)
def put(self, file):
print('Uploading %s...' % file)
obj = FtpServer()
obj.serve_forever()
从上面的例子可以看到hasattr及getattr的用法,另外还有delattr和setattr方法类似
优点
1、实现可插拔机制(对于代码来说),可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
2、动态导入模块(基于反射当前模块成员)
def run():
inp = input("请输入您想访问页面的url: ").strip()
modules, func = inp.split("/")
obj = __import__(modules)
if hasattr(obj, func):
func = getattr(obj, func)
func()
else:
print("404")
run()
请输入您想访问页面的url: commons/home
执行结果为:这是网站主页
实战案例
1.加载配置文件纯大写的配置
# 配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典
import settings
new_dict = {}
# print(dir(settings)) # dir获取括号中对象可以调用的名字
# ['AGE', 'INFO', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'desc', 'name']
for i in dir(settings):
if i.isupper(): # 如果名字是纯大写 那么获取该大写名字对应的值 'AGE' 'INFO'
v = getattr(settings, i)
new_dict[i] = v
print(new_dict)
2.模拟操作系统cmd终端执行用户命令
class WinCmd(object):
def dir(self):
print('dir获取当前目录下所有的文件名称')
def ls(self):
print('ls获取当前路径下所有的文件名称')
def ipconfig(self):
print('ipconfig获取当前计算机的网卡信息')
obj = WinCmd()
while True:
cmd = input('请输入您的命令>>>:')
if hasattr(obj, cmd):
cmd_name = getattr(obj, cmd)
cmd_name()
else:
print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)
接下来我们看看其他几个python内置的函数__getattr__、__setattr__、__delattr__
class Foo:
def __init__(self,name):
self.name = name
def __setattr__(self, key, value):
#添加/修改属性会触发它的执行
if isinstance(value,str):
self.__dict__[key] = value
print("__setattr__")
else:
raise TypeError("必须为字符串")
def __getattr__(self, item):
#只有在使用点调用属性且属性不存在的时候才会触发
print("getattr--->%s %s"%(item,type(item)))
def __delattr__(self, item):
#删除属性的时候会触发
self.__dict__.pop(item)
print("__delattr__")
f = Foo('yietong')
print(f.name)
f.age = '22'
print(f.age)
del f.age
print(f.__dict__)
print(f.xxxxx)
案例
需求:有一个 Point 类,查看它实例的属性,并修改它。动态为实例增加属性
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "Point:({},{})".format(self.x, self.y)
def show(self):
print(self, self.x, self.y)
p = Point(4, 5)
print(p) # Point:(4,5)
print(p.__dict__) # {'x': 4, 'y': 5}
p.__dict__['y'] = 16
print(p.__dict__) # {'x': 4, 'y': 16}
p.z = 10
print(p.__dict__) # {'x': 4, 'y': 16, 'z': 10}
print(dir(p))
print(sorted(p.__dir__()))
Point:(4,5)
{'x': 4, 'y': 5}
{'x': 4, 'y': 16}
{'x': 4, 'y': 16, 'z': 10}
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
使用内置函数改造
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "Point:({},{})".format(self.x, self.y)
def show(self):
print(self, self.x, self.y)
p = Point(4, 5)
print(p) # Point:(4,5)
print(p.__dict__) # {'x': 4, 'y': 5}
# p.__dict__['y'] = 16
setattr(p, 'y', 16)
print(p.__dict__) # {'x': 4, 'y': 16}
# p.z = 10
setattr(p, 'z', 10)
# print(p.__dict__) # {'x': 4, 'y': 16, 'z': 10}
print(getattr(p, '__dict__')) # {'x': 4, 'y': 16, 'z': 10}
# 动态调用方法
if hasattr(p, 'show'):
print(getattr(p, 'show')) # <bound method Point.show of <__main__.Point Object...>>
getattr(p, 'show')() # 调用此方法 Point:(4,16) 4 16
# 为类动态增加方法
if not hasattr(Point, 'add'):
setattr(Point, 'add', lambda self, other: Point(self.x + other.x, self.y + other.y))
print(Point.__dict__['add']) # 动态注入成功 <function <lambda> at ...>
print(Point.add) # 动态注入成功 <function <lambda> at ...>
p1 = Point(2, 3)
p2 = Point(5, 6)
print(p1.add) # 绑定方法 <bound method <lambda> of <__main__.Point object...>>
print(p1.add(p2)) # Point:(7,9)
# 为实例增加方法,未绑定,调用方法时,不能自动注入self
if not hasattr(p1, 'sub'):
setattr(p1, 'sub', lambda self, other: Point(self.x - other.x, self.y - other.y))
print(p1.sub) # <function <lambda> at ...>
print(p1.sub(p1, p2)) # 未绑定方法 Point:(-3,-3)
print(p1.__dict__)
print(Point.__dict__)
Point:(4,5)
{'x': 4, 'y': 5}
{'x': 4, 'y': 16}
{'x': 4, 'y': 16, 'z': 10}
<bound method Point.show of <__main__.Point object at 0x0000000002417490>>
Point:(4,16) 4 16
<function <lambda> at 0x00000000024CA160>
<function <lambda> at 0x00000000024CA160>
<bound method <lambda> of <__main__.Point object at 0x000000000244AF70>>
Point:(7,9)
<function <lambda> at 0x00000000024CA430>
Point:(-3,-3)
{'x': 2, 'y': 3, 'sub': <function <lambda> at 0x00000000024CA430>}
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000000024CA280>, '__str__': <function Point.__str__ at 0x00000000024CA310>, 'show': <function Point.show at 0x00000000024CA3A0>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x00000000024CA160>}
反射内建函数注意事项:实例方法绑定和非绑定的区别
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
return 'Point: <{}, {}>'.format(self.x, self.y)
p1 = Point(2, 3)
print(1, p1.show())
print(2, getattr(p1, 'show'))
# 动态给类增加方法
print(3, setattr(Point, 'showy', lambda self: 'y is {}.'.format(self.y)))
print(4, Point.showy)
print(5, p1.showy())
# 动态给实例增加方法
print(6, setattr(p1, 'showx', lambda self: 'x is {}.'.format(self.x)))
# print(7, p1.showx()) # <lambda>() missing 1 required positional argument: 'self'
print(8, p1.showx(p1))
# print(9, Point.showx(p1)) # AttributeError: type object 'Point' has no attribute 'showx'
# 注意绑定和不绑定的区别,绑定的方法会自动注入self
# 实例动态添加的方法是没有绑定效果的,所以不会自动注入self
print(10, p1.showy)
print(11, p1.showx)
print(12, getattr(p1, 'showy'))
print(13, getattr(p1, 'showx'))
# 方法一般都式定义在类上,一般不需要定义在实例上
1 Point: <2, 3>
2 <bound method Point.show of <__main__.Point object at 0x00000282E3516700>>
3 None
4 <function <lambda> at 0x00000282E25E1CA0>
5 y is 3.
6 None
8 x is 2.
10 <bound method <lambda> of <__main__.Point object at 0x00000282E3516700>>
11 <function <lambda> at 0x00000282E23531F0>
12 <bound method <lambda> of <__main__.Point object at 0x00000282E3516700>>
13 <function <lambda> at 0x00000282E23531F0>
动态增加属性方法的区别
通过 setattr
动态增加方法和装饰器装饰一个类、Mixin
方式的差异:setattr
是在运行时改变类或者实例的方式,但是装饰器或Mixin
都是定义时就决定了,因此反射能力具有更大的灵活性。