目录

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