反射
(1)反射的定义:
反射就是通过字符串来操作python代码中的对象的属性和方法。
(2)为什么要用反射?三个场景:
1.用户输入input
从用户输入的字符串中,想转换为变量的名字。
2.文件读取
从文件中读出的字符串,想转换为变量的名字。
3.网络
getattr()
先看看getattr()的源码的解释:


def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass
具体使用:
(1)getattr(obj,属性名)----> 传入字符串类型的属性名,返回属性的值,相当于执行了“obj.属性名”
(2)getattr(obj,方法名)----> 传入字符串类型的方法名,返回对应方法的内存地址。
谨记:如果第二个参数不存在的话,就会报错。
class A:
name = 'he'
age = 23
print(A.name) #he
print(A.age) #23
#使用反射的形式,操作类的属性
print(getattr(A, 'name'))
>>>
he
#使用反射操作类中的方法
class A:
number = 1
def func(self):
print('in the func')
a = A()
ret = getattr(a,'func') #得到了func方法的内存地址
ret() #调用func()方法
hasattr()


def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass
具体使用:
hasattr(obj,字符串类型的属性名/方法名)--->判断对象中是否拥有指定属性/方法,返回True/False。
谨记:所以hasattr经常和getattr一起使用,先用hasattr判断,再使用getattr取值。
#例子1:通过hasattr判断,再用getattr来取值。
class A:
name = 'he'
age = 23
inp = input('>>>:')
if hasattr(A,inp):
print(getattr(A,inp))
else:
print('A类中没有对应的属性')
>>>:name
he
>>>:sex
A类中没有对应的属性
#例子2
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key,self.__dict__[key]) # key self.__dict__[key]
s = Student('he',23)
# s.show()
if hasattr(s,'show'):
func = getattr(s,'show') #返回方法的内存地址,赋值给func()
func()
>>>
name he
age 23
setattr()


def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
具体使用:
setattr(obj,'name',value) ---->通过反射的方式为一个对象设置属性(增改操作),相当于obj.name=value
#正常情况下,想给对象设置一个值。
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key,self.__dict__[key]) # key self.__dict__[key]
s = Student('he',23)
s.sex = 'male'
print(s.sex)
>>>
male
#除了上面的方式之外,还有一种方式为对象设置属性,通过反射的形式.
setattr(s,'sex','male')
print(s.__dict__)
print(s.sex)
>>>
{'name': 'he', 'age': 23, 'sex': 'male'}
除了给对象绑定属性,还给对象绑定一个函数。
#创建一个函数,通过setattr给对象绑上这个函数
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key,self.__dict__[key]) # key self.__dict__[key]
s = Student('he',23)
def func1(a,b):
print(a,b)
setattr(s,'func',func1) #把普通函数当做函数添加到对象空间中
s.func(1,2)
print(s.__dict__)
>>>:
1 2
{'name': 'he', 'age': 23, 'func': <function func1 at 0x000001F16D252F28>}
delattr()
delattr(obj,'name'):通过反射的方式,删除对象中的属性(删除操作)
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key,self.__dict__[key]) # key self.__dict__[key]
s = Student('he',23)
#正常方法
del s.age
print(s.__dict__)
#通过delattr反射的方式
delattr(s,'age')
print(s.__dict__)
总结:
(1)hasattr():判断一个对象中是否能够调用一个名字,返回True/False
(2)getattr():返回一个对象中的名字的值
(3)setattr():为一个对象设置属性(增加/修改操作)
(4)delattr():删除一个对象的某个属性(删除操作)
通过反射来操作模块
通过上面学习的反射的四种方法,我们知道了如果通过反射来操作类中的属性/方法,但是除了"类名.属性名/方法名","对象名.属性名/方法名"之外,我们还接触过“模块名.函数名()”,那么能否通过反射来操作模块呢?答案是肯定的。
在python中,一切皆对象,当导入一个模块时,模块其实就可以看成一个对象。
getattr(模块,'属性名/方法名')
import sys
print(getattr(sys, 'path'))
>>>
C:\Python36\python.exe E:/python_training_s1/day21/day21_training.py
['E:\\python_training_s1\\day21', 'E:\\python_training_s1', 'C:\\Python36\\python36.zip',
'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36',
'C:\\Users\\hesihao\\AppData\\Roaming\\Python\\Python36\\site-packages',
'C:\\Python36\\lib\\site-packages',
'D:\\pycharm\\PyCharm 2018.1.4\\helpers\\pycharm_matplotlib_backend']
通过反射来操作当前文件
既然反射能够操作模块,那么当前文件其实也是一个模块,通过sys.modules可以看出,当前文件就是sys.modules['__main__']。
# 方式一:sys.modules[__main__]
import sys
name = 'he'
def foo():
print('这是本模块的函数foo')
print(getattr(sys.modules['__main__'], 'name'))
getattr(sys.modules['__main__'], 'foo')()
>>>
he
这是本模块的函数foo
通过sys.modules['__main__']好像完成了要求,但是,仔细想一下,使用__main__表示当前文件,假如在另外一个py文件中把当前文件导入,此时__main__便指向的是另外一个py文件,使用sys.modules['__main__']这种方式显然是不妥的,那么要怎么解决?
使用sys.modules[__name__]就能完美解决,因为__name__就是'__main__'。
#通过sys.modules[__name__]反射当前模块
import sys
name = 'he'
def foo():
print('这是本模块的函数foo')
getattr(sys.modules[__name__],'foo')()
>>>
这是本模块的函数foo
总结:
sys.modules['__main__'] : __main__会随着文件的不同而不同,存在安全隐患。
sys.modules[__name__]:__name__不管在哪里导入这个模块,都代表这个文件。
如何反射来操作当前模块中的类
import sys
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key,self.__dict__[key]) # key self.__dict__[key]
main = sys.modules[__name__]
cls = getattr(main,'Student')
he = cls('小何',23)