热门IT资讯网

32面向对象7_reflection

发表于:2024-11-25 作者:热门IT资讯网编辑
编辑最后更新 2024年11月25日,目录reflection相关的内建函数:... 1反射相关的魔术方法(__getattr__()、__setattr__()、__delattr__()):... 7反射相关的魔术方法(__getat

目录

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

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

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

reflection反射

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

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

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

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

注:

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

reflection相关的内建函数:

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

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


0