32面向对象7_reflection-创新互联

目录

公司主营业务:成都网站建设、网站设计、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出浮山免费做网站回馈大家。

reflection相关的内建函数:...1

反射相关的魔术方法(__getattr__()、__setattr__()、__delattr__()):...7

反射相关的魔术方法(__getattribute__):...9

reflection反射

指运行时,获取类型定义信息;运行时通过实例能查出实例及所属类的类型相关信息;

一个对象能够在运行时,像照镜子一样,反射出它的所有类型信息;

简单说,在python中,能通过一个对象,找出其type、class、attribute、method的能力,称为反射或自省;

具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr();

注:

运行时,区别于编译时,指程序被加载到内存中执行的时候;

reflection相关的内建函数:

getattr(object,name[,default]),通过name返回object的属性值,当属性不存在,将使用default返回,如果没有default,则抛AttributeError,name必须为字符串;

setattr(object,name,value),object(实例或类)属性存在则覆盖,不存在新增;

hasattr(object,name),判断对象是否有这个名字的属性,name必须为字符串;

动态添加属性方式,与装饰器修改一个类、mixin方式的差异?

动态添加属性,是运行时改变类或者实例的方式,因此反射能力具有更大的灵活性;

装饰器和mixin,在定义时就决定了;

注:

一般运行时动态增,很少删;

self.__class__,同type(self);即实例a.__class__,同type(a);

例:

class Point:

def __init__(self,x,y):

self.x = x

self.y = y

def __str__(self):

return 'Point({},{})'.format(self.x,self.y)

__repr__ = __str__

def show(self):

print(self.x,self.y)

p = Point(4,5)

print(p.__dict__)

p.__dict__['y'] = 16

print(p.__dict__['y'])   #通过实例的属性字典__dict__访问实例的属性,本质上是利用反射的能力,这种访问方式不优雅,python提供了相关的内置函数

p.z = 10

print(p.__dict__)

p1 = Point(4,5)

p2 = Point(10,10)

print(repr(p1),repr(p2))

print(p1.__dict__)

setattr(p1,'y',16)

setattr(p1,'z',18)

print(getattr(p1,'__dict__'))

if hasattr(p1,'show'):   #动态调用方法

getattr(p1,'show')()

if not hasattr(Point,'add'):   #源码中常用此方式

setattr(Point,'add',lambda self,other: Point(self.x + other.x,self.y + other.y))

print(Point.add)

print(p1.add)

print(p1.add(p2))

输出:

{'x': 4, 'y': 5}

16

{'x': 4, 'y': 16, 'z': 10}

Point(4,5) Point(10,10)

{'x': 4, 'y': 5}

{'x': 4, 'y': 16, 'z': 18}

4 16

at 0x7f2d82572e18>

of Point(4,16)>

Point(14,26)

例:

class A:

def __init__(self,x):

self.x = x

a = A(5)

setattr(A,'y',10)   #运行时改变属性,在类上操作

print(A.__dict__)

print(a.__dict__)

print(getattr(a,'x'))

print(getattr(a,'y'))   #实例没有y,向上找自己类的

# print(getattr(a,'z'))   #X

print(getattr(a,'z',100))

setattr(a,'y',1000)   #在实例上操作

print(A.__dict__)

print(a.__dict__)

# setattr(a,'mtd',lambda self: 1)   #在实例上定义方法,看似可以,实际不行,未绑定self,若要在调用时不出错,需把实际名写上,如a.mtd(a)

# a.mtd()   #X

# print(a.mtd(a))   #V

print(a.__dict__)

setattr(A,'mtd',lambda self: 2)   #在类上定义方法没问题

print(a.mtd())

print(A.__dict__)

输出:

{'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None, 'y': 10}

{'x': 5}

5

10

100

{'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None, 'y': 10}

{'x': 5, 'y': 1000}

{'x': 5}

2

{'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None, 'mtd': at 0x7fde274bfe18>}

习题:

命令分发器,通过名称找对应的函数执行;

思路:名称找对象的方法;

函数方式实现:

方1:

def dispatcher():

cmds = {}

def reg(cmd,fn):

if isinstance(cmd,str):

cmds[cmd] = fn

else:

print('error')

def run():

   while True:

cmd = input('plz input command: ')

if cmd.strip() == 'quit':

return

print(cmds.get(cmd.strip(),defaultfn)())

def defaultfn():

return 'default function'

return reg,run

reg,run = dispatcher()

reg('cmd1',lambda : 1)

reg('cmd2',lambda : 2)

run()

输出:

plz input command: cmd3

default function

plz input command:  cmd2

2

plz input command: cmd1

1

plz input command: quit

方2:

def cmds_dispatcher():

cmds = {}

def reg(name):

def wrapper(fn):

cmds[name] = fn

return fn

return wrapper

def dispatcher():

while True:

cmd = input('plz input comd: ')

if cmd.strip() == 'quit':

return

print(cmds.get(cmd.strip(),defaultfn)())

def defaultfn():

return 'default function'

return reg,dispatcher

reg,dispatcher = cmds_dispatcher()

@reg('cmd1')

def foo1():

return 1

@reg('cmd2')

def foo2():

return 2

dispatcher()

面向对象方式实现:

使用setattr()和getattr()找到对象的属性(实际是在类上加的,实例找不到逐级往上找),比自己维护一个dict来建立名称和函数之间的关系要好;

实现1:

class Dispatcher:

def cmd1(self):

return 1

def reg(self,cmd,fn):

if isinstance(cmd,str):

setattr(self.__class__,cmd,fn)   #放在类上最方便,self.__class__同type(self);不要在实例上定义,如果在实例上,setattr(self,cmd,fn),调用时要注意dis.reg('cmd2',lambda : 2)

else:

print('error')

def run(self):

while True:

cmd = input('plz input cmd: ')

if cmd.strip() == 'quit':

return

print(getattr(self,cmd.strip(),self.defaultfn)())

def defaultfn(self):

return 'default function'

dis = Dispatcher()

dis.reg('cmd2',lambda self: 2)

dis.run()

# print(dis.__class__.__dict__)

# print(dis.__dict__)

输出:

plz input cmd: cmd1

1

plz input cmd: cmd2

2

plz input cmd: cmd3

default function

plz input cmd: 11

default function

实现2:

class Dispatcher:

def __init__(self):

self._run()

def cmd1(self):

return 1

def cmd2(self):

return 2

def reg(self,cmd,fn):

if isinstance(cmd,str):

setattr(self.__class__,cmd,fn)

else:

print('error')

def _run(self):

while True:

cmd = input('plz input cmd: ')

if cmd.strip() == 'quit':

return

print(getattr(self,cmd.strip(),self.defaultfn)())

def defaultfn(self):

return 'default function'

Dispatcher()

输出:

plz input cmd: cmd1

1

plz input cmd: cmd2

2

plz input cmd: cmd3

default function

plz input cmd: abcd

default function

反射相关的魔术方法(__getattr__()、__setattr__()、__delattr__()):

__getattr__(),当在实例、实例的类及祖先类中查不到属性,才调用此方法;

__setattr__(),通过点访问属性,进行增加、修改都要调用此方法;

__delattr__(),当通过实例来删除属性时调用此方法,删自己有的属性;

一个类的属性会按照继承关系找,如果找不到,就是执行__getattr__(),如果没有这个方法,抛AttributeError;

查找属性顺序为:

instance.__dict__-->instance.__class__.__dict__-->继承的祖先类直到object的__dict__-->调用__getattr__(),如果没有__getattr__()则抛AttributeError异常;

__setattr__()和__delattr__(),只要有相应的操作(如初始化时self.x = x或运行时a.y = 200触发__setattr__(),del a.m则触发__delattr__())就会触发,做拦截用,拦截做增加或修改,属性要加到__dict__中,要自己完成;

这三个魔术方法的第一个参数为self,则如果用类名.属性操作时,则这三个魔术方法管不着;

例:

class A:

m = 6

def __init__(self,x):

self.x = x

def __getattr__(self, item):   #对象的属性按搜索顺序逐级找,找到祖先类object上也没有对应属性,则最后找__getattr__(),如有定义__getattr__()返回该函数返回值,如果没有此方法,则报错AttributeError: 'A' object has no attribute 'y'

print('__getattr__',item)

print(A(10).x)

print(A(8).y)

输出:

10

__getattr__ y

None

例:

class A:

m = 6

def __init__(self,x):

self.x = x   #__init__()中的self.x = x也调用__setattr__()

def __getattr__(self, item):

print('__getattr__',item)

# self.__dict__[item] = 'default_value'

def __setattr__(self, key, value):

print('__setattr__',key,value)

# self.__dict__[key] = value

def __delattr__(self, item):

print('__delattr__')

a = A(8)   #初始化时的self.x = x也调用__setattr__()

a.x   #调用__getattr__()

a.x = 100   #调用__setattr__(),实例的__dict__为空没有x属性,虽有触发__setattr__(),但没写到__dict__中,要自己写

a.x

a.y

a.y = 200

a.y

print(a.__dict__)   #__getattr__()和__setattr__()都有,实例的__dict__为空,用a.x访问属性时按顺序都没找到最终调用__getattr__()

print(A.__dict__)

del a.m

输出:

__setattr__ x 8

__getattr__ x

__setattr__ x 100

__getattr__ x

__getattr__ y

__setattr__ y 200

__getattr__ y

{}

{'__module__': '__main__', 'm': 6, '__init__': , '__getattr__': , '__setattr__': , '__dict__': , '__weakref__': , '__doc__': None}

__delattr__

反射相关的魔术方法(__getattribute__):

__getattribute__(),实例所有属性调用,都从这个方法开始;

实例的所有属性访问,第一个都会调用__getattribute__()方法,它阻止了属性的查找,该方法应该返回(计算后的)值或抛AttributeError,它的return值将作为属性查找的结果,如果抛AttributeError则直接调用__getattr__(),表示属性没有找到;

为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,如object.__getattribute__(self,name);

除非明确知道__getattribute__()方法用来做什么,否则不要使用它,拦截面太大;

属性查找顺序:

实例调用__getattribute__()-->instance.__dict__-->instance.__class__.__dict__-->继承的祖先类直到object的__dict__-->调用__getattr__();

例:

class A:

m = 6

def __init__(self,x):

self.x = x

def __getattr__(self, item):

print('__getattr__',item)

# self.__dict__[item] = 'default_value'

def __setattr__(self, key, value):

print('__setattr__',key,value)

# self.__dict__[key] = value

def __delattr__(self, item):

print('__delattr__')

def __getattribute__(self, item):

print('__getattribute__',item)

raise AttributeError(item)   #如果抛AttributeError则直接调用__getattr__(),表示属性没找到

# return self.__dict__[item]   #递归调用

# return object.__getattribute__(self,item)   #继续向后找,这样做没意义

a = A(8)

a.x

a.y

a.z

输出:

__setattr__ x 8

__getattribute__ x

__getattr__ x

__getattribute__ y

__getattr__ y

__getattribute__ z

__getattr__ z

另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


文章名称:32面向对象7_reflection-创新互联
文章位置:http://myzitong.com/article/digjpp.html