day7-反射
产生背景
通过字符串映射或修改程序运行时的状态,属性,方法
需求:我们需要通过用户输入的字符串来调用eat()方法
class Dog(object): def __init__(self,name): self.name = name def eat(self): print("%s is eating..."%self.name) d = Dog("Jacky") choice = input(">>:").strip() d.choice #输出 >>:eat Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python/module_3/反射.py", line 12, in <module> d.choice AttributeError: 'Dog' object has no attribute 'choice'
解析:由于choice是一个str,所以它不能直接被对象调用,那么问题来了,我们需要根据用户的输入去调用相应的方法,应该怎么办?
尝试:通过添加If...else进行判断
if choice == "eat": d.eat() #输出 >>:eat Jacky is eating...
但是,如果我们有很多对象的话,此方法就显得力不从心了,那么我们如何实现将用户输入的字符和方法进行匹配调用?
这里就用到了反射,这里需要分2步进行:
1.hasattr(obj,str):判断一个对象obj里是否有对应的str的字符串的方法
print(hasattr(d,choice)) #hasattr(对象,输入的字符) #输出 >>:eat True >>:talk False
2.getattr(obj,str):根据字符串去获取obj对象里的对应字符串方法名的内存地址
print(getattr(d,choice)) #输出 >>:eat <bound method Dog.eat of <__main__.Dog object at 0x00000000007D6128>> >>:talk Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python/module_3/反射.py", line 13, in <module> print(getattr(d,choice)) AttributeError: 'Dog' object has no attribute 'talk'
解析:可以看到,如果用户输入的字符串在对象属性里面存在,就会反射它在内存中对象的内存地址,此时,还没有被调用,我们只需要加上括号()就可以调用此方法了。
反射函数
用法:首先通过函数hasattr判断字符串是否存在,如果存在再通过getattr进行调用
class Dog(object): def __init__(self,name): self.name = name def eat(self,food): print("%s is eating %s"%(self.name,food)) d = Dog("Jacky") choice = input(">>:").strip() if hasattr(d,choice): getattr(d,choice)() #修正写法 if hasattr(d,choice): func = getattr(d,choice) func(“baozi”) #func()里面也可以传递一些参数 #输出 >>:eat Jacky is eating baozi
除了以上2种反射,另外我们可以介绍下面2个方法:
动态装配
1.装配方法
setattr(x,y,z)
用法:如果在对象的方法中没有我们输入的方法,可以将类外面的方法通过字符串的形式动态的装配到类中。
#类Dog外面的方法
def bulk(self):
print("%s is yelping...."%self.name)
class Dog(object):
#类Dog里面的方法
def __init__(self,name):
self.name = name
def eat(self,food):
print("%s is eating %s"%(self.name,food))
d = Dog("Jacky")
choice = input(">>:").strip()
if hasattr(d,choice):
getattr(d,choice)("baozi")
else:
setattr(d,choice,bulk) #因为相当于x.y=v,如果执行时输入的是talk也就是d.talk=bulk(内存地址)
d.talk(d) #1
#输出
>>:talk
Jacky is yelping...
解析:在1处如果写talk,那么程序就把talk当作关联在类中的方法名,然后将类Dog外的bulk方法传进类Dog中,所以1处写的是talk(),那么调用方法的时候就是talk(),此时bulk()的bulk相当于是一个变量名,bulk使用talk来代替。
注意:在d.talk()调用时,一定要把对象d传递进去,因为它不在类中,对象d(self)不会自动传递。
那么,我们是否可以在装配方法时,使用d.bulk()来调用def bulk(self)方法呢?
print("def bulk", bulk) #打印方法bulk的内存地址
d = Dog("Jacky")
choice = input(">>:").strip()
if hasattr(d,choice):
func = getattr(d,choice)
func()
else:
setattr(d,choice,bulk) #相当于d.talk=bulk
func = getattr(d,choice)
print("d.choice:",d.talk) #d.talk = bulk(内存地址)
#d.bulk() #不能直接使用d.bulk()调用外面的方法,因为bulk()只是装配到类中,无法直接调用,需要使用对象来调用,也就是说d.bulk(d)就可以了
print("func:",func)
print("bulk:",bulk)
func()
#输出
针对上面的问题,我们进行debug查看:
解析:反射的作用是将字符串反射成在内存中该方法的地址,因为用户输入的是str,不是内存对象,现在可以使用反射实现方法的调用,而不是之前我们使用if...else和startswith进行字符串匹配判断。
2.装配属性
2.1对象属性不存在的情况下
class Dog(object): def __init__(self,name): self.name = name def eat(self,food): print("%s is eating %s"%(self.name,food)) d = Dog("Jacky") choice = input(">>:").strip() if hasattr(d,choice): func = getattr(d,choice) func("baozi") else: setattr(d,choice,22) #setattr(对象obj,输入的字符串str(属性名),属性的值)相当于x.y = v print(getattr(d,choice)) #打印出choice属性的值(上面v为22) #输出 >>:yes #任意(类不存在的属性) 22
2.2对象属性存在的情况下
class Dog(object): def __init__(self,name): self.name = name def eat(self,food): print("%s is eating %s"%(self.name,food)) d = Dog("Jacky") choice = input(">>:").strip() if hasattr(d,choice): func = getattr(d,choice) func("baozi") else: setattr(d,choice,22) print(getattr(d,choice)) #输出 >>:name Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python/module_3/反射.py", line 16, in <module> getattr(d,choice)("baozi") TypeError: 'str' object is not callable
解析:因为name属性原本就存在,且类型为str,通过debug看到在执行到func时已经得到name的值"Jacky",但是由于str不能被调用,所以报错!
我们可以对其属性进行修改
d = Dog("Jacky") #生成一个对象d
choice = input(">>:").strip() #用户输入
if hasattr(d,choice): #判断属性是否存在
func = getattr(d,choice)
# func("baozi")
setattr(d,choice,"Maria") #重新修改属性值
print(d.name)
else:
# setattr(d,choice,bulk)
# d.talk(d)
setattr(d,choice,22)
print(getattr(d,choice))
#输出
>>:name
Maria
>>:go
22
删除对象属性
使用delattr(x,y)函数对以上代码做修改后,可以看到对象属性因为已经被删除,所以提示该对象没有这个属性
if hasattr(d,choice): delattr(d,choice) #删除对象属性 else: setattr(d,choice,22) print(getattr(d,choice)) print(d.name) #打印出对象属性 #输出 >>:name Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python/module_3/反射.py", line 24, in <module> print(d.name) AttributeError: 'Dog' object has no attribute 'name'