python编程 基础入门学习笔记八——反射

本节笔记

Python的反射机制

1).hasattr

2).getattr

3).setattr

4).delattr

 

Python的反射机制                                                                                                                                                     

 反射定义

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

反射的实现

Python面向对象编程中的反射:通过字符串的形式,操作对象相关的属性。

Python中一切皆对象,所以都可以用反射机制。

下面逐一介绍4个可以实现自省的函数(下述方法适用于类和对象——一切皆对象,类本身也是一个对象):

1)hasattr(obj,name)

  判断一个对象里面是否有name属性或name方法,返回bool值(有则返回True,没有就返回False)。需要注意的是这里name需要用引号,也就是说需要是一个字符串的形式。

>>> class ClassName(object):
    name='Lily'
    def __init__(self):
        pass
    def func(self,name):
        print("I am %s" %name)

        
>>> obj=ClassName()
>>> print(hasattr(obj,"bar"))
False
>>> print(hasattr(obj,"name"))
True
>>> print(hasattr(obj,"func"))
True

 

2)getattr(obj,name,default=None)

  获取对象obj的属性或者方法,若存在,则获取对象obj的属性或者方法的内存地址,若不存在会有异常而报错。

class ClassName(object):
    name='Lily'
    def __init__(self):
        pass
    def func(self,name):
        print("I am %s" %name)
        
obj=ClassName()
prop=getattr(obj,"name")
print(prop)
func=getattr(obj,"func")
func("Jack")
getattr(obj,"bar")    #异常,报错

需要注意的是,如果是获取对象的方法,如果需要运行这个方法,在后面添加一对括号及对应参数。

3)setattr(obj,name,values)

  给对象的属性赋值,若这个属性不存在,则先创建再赋值;若这个属性已经存在,则覆盖原属性值。

#-*- coding:utf-8 -*-

class ClassName(object):
    name='Lily'
    def __init__(self):
        pass
    def func(self,name):
        print("I am %s" %name)

obj=ClassName()

setattr(obj,'sex','Female')   #动态装载一个属性
print('sex=',getattr(obj,'sex'))  #获取属性值

setattr(obj,'name','David')   #name属性在类中已存在
print('name=',getattr(obj,'name'))  #覆盖原类中的name属性值

  给对象动态装载方法,当在希望给已有类添加一个新的方法,可以用setattr(obj,name,values),

#-*- coding:utf-8 -*-

def add_func(self):
    print("add_func")

class ClassName(object):
    bar='abc'
    def __init__(self,name):
        self.name=name
    def func(self,name):
        print("I am %s" %self.name)

obj=ClassName("Jack")

setattr(obj,'add_func',add_func) #动态装载一个方法
obj.add_func(obj)    #执行

需要注意的是,若这个方法已经存在,则存在覆盖的风险,所以建议在 setattr 前先进行判断是否方法已经存在,再根据实际情况设计。

4)delattr(obj,name)

  删除obj对象名为name的属性。

#-*- coding:utf-8 -*-

class ClassName(object):
    bar='abc'
    def __init__(self,name):
        self.name=name
    def func(self,name):
        print("I am %s" %self.name)

obj=ClassName("Jack")
choice=input(">>:").strip()
print(hasattr(obj,choice))  #判断一个对象obj里是否有对应的choice字符串的方法

if hasattr(obj,choice):
    delattr(obj,choice)
    print(hasattr(obj,choice))

需要注意的是,这里只能删除普通属性name,无法删除静态属性bar。

 

#-*- coding:utf-8 -*-
#Author:'Yang'

class BlackMedia(object):
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_house(self):
        print('%s 卖房子'%self.name)
    def rent_house(self):
        print('%s 租房子'%self.name)

obj=BlackMedia('易居','闸北')

#检查是否含有某属性或方法
print(hasattr(obj,"name"))  #属性
print(hasattr(obj,"sell_house"))    #方法

#获取属性或方法
n=getattr(obj,"name")   #属性
print(n)
func=getattr(obj,"rent_house")  #方法
func()

#设置(动态装载)属性
print(obj.__dict__) #装载前
setattr(obj,'flag',True)
setattr(obj,'show_name',lambda self:self.name+'123')
print(obj.__dict__) #装载后
print(obj.show_name(obj))

#删除属性
delattr(obj,'addr')
delattr(obj,'show_name')
print(obj.__dict__)
练习1
True
True
易居
易居 租房子
{'addr': '闸北', 'name': '易居'}
{'show_name': <function <lambda> at 0x005D4C48>, 'addr': '闸北', 'flag': True, 'name': '易居'}
易居123
{'flag': True, 'name': '易居'}
代码执行结果
#-*- coding:utf-8 -*-

class Foo(object):
    staticField='old'

    def __init__(self):
        self.name='Lily'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'

print(getattr(Foo,'staticField'))
func=getattr(Foo,'func')
print(Foo.func(Foo))
bar=getattr(Foo,'bar')
print(Foo.bar())
类也是对象
#-*- coding:utf-8 -*-

import sys

def f1():
    print("f1")

def f2():
    return 'f2'

this_module=sys.modules[__name__]

print(hasattr(this_module,'f1'))
print(getattr(this_module,'f1'))

m=getattr(this_module,'f2')
print(m())
模块的反射

反射机制的好处

  实现可插拔机制

   可以事先定义好接口,接口在被完善后才会真正执行,从而实现了即插即用。这其实是一种“后期绑定”。也就是说,我们可以事先定义接口把主要的逻辑写好,后期再做接口功能的实现。

举个例子,程序员A定义了一个接口,但是未实现功能

#-*- coding:utf-8 -*-

#程序员A编写
class FtpClient(object):
    '这是程序员A写的ftp客户端框架,暂未实现具体功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr
程序员A编写

程序员B不想等到A完全实现功能再运行,这时利用反射事先做判断,不管程序员A的功能是否实现,都不影响程序员B继续实现其他逻辑。

#这个是程序员B
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('没有这个方法')
    print('处理其他的逻辑')
程序员B编写

如果程序员A未完成的功能希望程序员C实现,程序员C也可以在不改变程序员A的代码情况下,利用反射,也可以完成。

#这个是程序员C
def get():
    return '---get---'

f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('没有这个方法')
    # print('处理其他的逻辑')
    print("创建这个方法")
    setattr(f1,'get',get)
    print(f1.get())
程序员C编写

  动态导入模块(基于反射当前模块成员) 

#-*- coding:utf-8 -*-

import sys

def f1():
    print("f1")

def f2():
    return 'f2'
module1.py
#-*- coding:utf-8 -*-

con,action=input("module_addr:").split('/')
module=__import__(con)    #动态导入模块,等价于import con as module
func=getattr(module,action)
res=func()  #获取模块中函数的返回值
print(res)

输出:

addr:module1/f2   <---这里需要键盘输入
f2

import sys
__import__('theModuleFullName')  #动态地导入模块
m=sys.modules['theModuleFullName']  #得到这个模块
attstr=dir(m)   #得到属性的列表
for str in attstr:  #迭代之
    att=getattr(m,str)
    #如果是类,而且是Father的子类
    if type(att)==type and issubclass(att,Father):
        theObj=att()    #实例化
 theObj.doSomething()   #开始调用
小结

 

posted on 2017-06-28 16:16  奔跑的蜗牛~~  阅读(103)  评论(0)    收藏  举报

导航