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'
posted @ 2017-09-30 16:20  Mr.hu  阅读(87)  评论(0编辑  收藏  举报