zoukankan      html  css  js  c++  java
  • Python魔术方法——反射

    init返回值一般不写return,要写只能写return,或者是return None。

    概述

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

    反射,reflection,指的是运行时获取类型定义信息。

    一个对象能够在运行时,像照镜子一样,反射出其类型信息。

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

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

    反射相关的函数和方法

    需求

    有一个Point类,查看它实例的属性,并修改它。动态为实例增加属性。

    class Point():
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __str__(self):
            return "Point({},{})".format(self.x,self.y)
        
        def show(self):
            print(self.x,self.y)
            
    p = Point(4,5)
    print(p)
    print(p.__dict__)
    p.__dict__["y"] = 16
    print(p.__dict__)
    p.z = 10
    print(p.__dict__)
    print(dir(p))#ordered list
    print(p.__dir__())#list
    
    结果为:
    Point(4,5)
    {'x': 4, 'y': 5}
    {'x': 4, 'y': 16}
    {'x': 4, 'y': 16, 'z': 10}
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
    ['x', 'y', 'z', '__module__', '__init__', '__str__', 'show', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

    上例通过属性字典__dict__来访问对象的属性,本质上也是利用的反射的能力。

    但是,上面的例子中,访问的方式不优雅,Python提供了内置的函数。

    getattr(object,name,[default]):通过name返回object的属性值。当属性不存在,将返回default返回,如果没有default,则抛出attributeerror错误,name必须为字符串。

    setattr(object,name,vaule):object的属性存在,则覆盖,不存在,新增。

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

    class A:
        def __init__(self):
            self.x = 5
    
    a = A()
    setattr(A,"y",10)
    print(A.__dict__)
    print(a.__dict__)
    print(getattr(a,"x"))
    print(getattr(a,"y"))
    if hasattr(a,"z"):
        print(getattr(a,"z"))
        
    setattr(a,"y",1000)
    print(A.__dict__)
    print(a.__dict__)
    
    结果为:
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000059A3D90>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5}
    5
    10
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000059A3D90>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5, 'y': 1000}

    用上面的方法来修改上例的代码

    class Point():
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __str__(self):
            return "point({},{})".format(self.x,self.y)
        
        def show(self):
            print(self)
            
    p1 = Point(4,5)
    p2 = Point(10,10)
    print(repr(p1),repr(p2),sep="
    ")
    print(p1.__dict__)
    setattr(p1,"y",16)
    setattr(p1,"z",10)
    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))#绑定
    
    
    #为实例增加方法,为绑定
    if not hasattr(p1,"sub"):
        setattr(p1,"sub",lambda self,other:Point(self.x-other.x,self.y-other.y))
        
    print(p1.sub(p1,p1))
    print(p1.sub)
    
    #add在谁里面,sub在谁里面
    print(p1.__dict__)
    print(Point.__dict__)
    
    结果为:
    <__main__.Point object at 0x0000000005A98940>
    <__main__.Point object at 0x0000000005A98898>
    {'x': 4, 'y': 5}
    {'x': 4, 'y': 16, 'z': 10}
    point(4,16)
    <function <lambda> at 0x0000000005A9C400>
    <bound method <lambda> of <__main__.Point object at 0x0000000005A98940>>
    point(14,26)
    point(0,0)
    <function <lambda> at 0x0000000005A6E598>
    {'x': 4, 'y': 16, 'z': 10, 'sub': <function <lambda> at 0x0000000005A6E598>}
    {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000000005A9CBF8>, '__str__': <function Point.__str__ at 0x0000000005A9CAE8>, 'show': <function Point.show at 0x0000000005A9CA60>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x0000000005A9C400>}

    思考,这种动态增加属性的方式和装饰器修饰一个类,mixin方式的差异?

    这种动态增删属性的方式是运行时改变或者实例的方式,但是装饰器或mixin都是定义时就决定了,因此反射能力具有更大的灵活性。

    练习:命令分发器,通过名称找对应的函数执行。

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

    class dispatcher():
        
        def cmd1(self):
            print("cmd1")
            
        def run(self):
            while True:
                cmd = input("please input command: ")
                if cmd.strip() =="quit":
                    return 
                getattr(self,cmd.strip(),self.defaultfn)()
                
        def defaultfn(self):
            print("default")
            
    dis = dispatcher()
    dis.run()
    
    结果为:
    please input command: cmd1
    cmd1
    please input command: a
    default
    please input command: cdm
    default
    please input command: quit

    下面的是可以实现自己注册的。

    class dispatcher():
        
        def cmd1(self):
            print("cmd1")
            
        def reg(self,cmd,fn):
            if isinstance(cmd,str):
                setattr(self.__class__,cmd,fn)
            else:
                print("error")
            
        def run(self):
            while True:
                cmd = input("please input command: ")
                if cmd.strip() =="quit":
                    return 
                getattr(self,cmd.strip(),self.defaultfn)()
                
        def defaultfn(self):
            print("default")
            
    dis = dispatcher()
    dis.reg("cmd2",lambda self:print(2))
    dis.reg("cmd3",lambda self:print(3))
    dis.run()
    
    结果为:
    please input command: cmd1
    cmd1
    please input command: cmd3
    3
    please input command: cmd2
    2
    please input command: cd2
    default
    please input command: quit
    class Dispatcher:
        def __init__(self):
            self._run()
            
        def cmd1(self):
            print("i'm cmd1")
        def cmd2(self):
            print("i'm cmd2")
            
        def _run(self):
            while True:
                cmd = input("plz imput a command:").strip()
                if cmd =="quit":
                    break
                getattr(self,cmd,lambda:print("unknown command {}".format(cmd)))()
                
    Dispatcher()
    
    结果为:
    plz imput a command:1
    unknown command 1
    plz imput a command:3
    unknown command 3
    plz imput a command:quit
    <__main__.Dispatcher at 0x5825940>

    上例中使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名称和函数之间的关系的的方式好多了。

    class A:
        def __init__(self):
            self.x = 5
    
    a = A()
    setattr(A,"y",10)
    print(A.__dict__)
    print(a.__dict__)
    print(getattr(a,"x"))
    print(getattr(a,"y"))
    if hasattr(a,"z"):
        print(getattr(a,"z"))
        
    setattr(a,"y",1000)
    print(A.__dict__)
    print(a.__dict__)
    
    setattr(a,"mtd",lambda self:1)
    print(A.__dict__)
    print(a.__dict__)
    
    结果为:
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000059A3C80>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5}
    5
    10
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000059A3C80>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5, 'y': 1000}
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x00000000059A3C80>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5, 'y': 1000, 'mtd': <function <lambda> at 0x00000000059E4400>}

    上例中的方法添加到了实例的属性中。但是调用的时候会出错,要不出错。

    class A:
        def __init__(self):
            self.x = 5
    
    a = A()
    setattr(A,"y",10)
    print(A.__dict__)
    print(a.__dict__)
    print(getattr(a,"x"))
    print(getattr(a,"y"))
    if hasattr(a,"z"):
        print(getattr(a,"z"))
        
    setattr(a,"y",1000)
    print(A.__dict__)
    print(a.__dict__)
    
    setattr(A,"mtd",lambda self:1)
    print(A.__dict__)
    print(a.__dict__)
    print(a.mtd())
    
    结果为:
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x0000000005C3DD90>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5}
    5
    10
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x0000000005C3DD90>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10}
    {'x': 5, 'y': 1000}
    {'__module__': '__main__', '__init__': <function A.__init__ at 0x0000000005C3DD90>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'y': 10, 'mtd': <function <lambda> at 0x0000000005C3D6A8>}
    {'x': 5, 'y': 1000}
    1

    反射相关的魔术方法

    __getattr__(),__setattr__(),__delattr__()这三个魔术方法,分别测试。

    __getattr__()方法

    class Base:
        n = 0
        
    class Point(Base):
        z= 6
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def show(self):
            print(self.x,self.y)
            
        def __getattr__(self,item):
            return "missing {}".format(item)
        
    p1 = Point(4,5)
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    
    结果为:
    
    4
    6
    0
    missing t

    一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__方法,如果没有这个方法。就会抛出attributeerror异常,表示找不到属性。

    查找属性顺序为:

     __setattr__()方法

    class Base:
        n = 0
        
    class Point(Base):
        z= 6
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def show(self):
            print(self.x,self.y)
            
        def __getattr__(self,item):
            return "missing {}".format(item)
        
        def __setattr__(self,key,value):
            print("setattr {} = {}".format(key,value))
        
    p1 = Point(4,5)
    print(p1.x)#missing ,why
    print(p1.z)
    print(p1.n)
    print(p1.t)#missing
    
    p1.x = 50
    print(p1.__dict__)
    p1.__dict__["x"] = 60
    print(p1.__dict__)
    print(p1.x)
    
    结果为:
    setattr x = 4
    setattr y = 5
    missing x
    6
    0
    missing t
    setattr x = 50
    {}
    {'x': 60}
    60
     

    实例通过.点设置属性,如同self.x = x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成。

    class Base:
        n = 5
        
    class A(Base):
        m = 6
        def __init__(self,x):
            self.x = x
            
        def __setattr__(self,key,value):
            print("__setattr__",key,value)
            
    a  = A(10)
    a.x = 100
            
    
    结果为:
    __setattr__ x 10
    __setattr__ x 100
    class Base:
        n = 5
        
    class A(Base):
        m = 6
        def __init__(self,x):
            self.x = x
            
        def __setattr__(self,key,value):
            print("__setattr__",key,value)
            
        def __getattr__(self,item):
            print("__getattr__",item)
            
    a  = A(10)
    a.x = 100
    a.y = 200
    print(a.__dict__)
    a.y
    
    结果为:
    __setattr__ x 10
    __setattr__ x 100
    __setattr__ y 200
    {}
    __getattr__ y

    为什么后面的a.y没有访问到。

    class Point(Base):
        z= 6
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def show(self):
            print(self.x,self.y)
            
        def __getattr__(self,item):
            return "missing {}".format(item)
        
        def __setattr__(self,key,value):
            print("setattr {} = {}".format(key,value))
            self.__dict__[key] = value
        
    p1 = Point(4,5)
    print(p1.x)#missing ,why
    print(p1.z)
    print(p1.n)
    print(p1.t)#missing
    
    p1.x = 50
    print(p1.__dict__)
    p1.__dict__["x"] = 60
    print(p1.__dict__)
    print(p1.x)
    
    结果为:
    setattr x = 4
    setattr y = 5
    4
    6
    0
    missing t
    setattr x = 50
    {'x': 50, 'y': 5}
    {'x': 60, 'y': 5}
    60

    __setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__.

    __delattr__()

    class Point(Base):
        z= 5
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __delattr__(self,item):
            print("can not del {}".format(item))
        
    p = Point(4,5)
    del p.x
    p.z =15
    del p.z
    del p.z
    
    
    print(Point.__dict__)
    print(p.__dict__)
    del Point.z
    print(Point.__dict__)
    
    结果为:
    can not del x
    can not del z
    can not del z
    {'__module__': '__main__', 'z': 5, '__init__': <function Point.__init__ at 0x0000000005F3BD90>, '__delattr__': <function Point.__delattr__ at 0x0000000005F3BE18>, '__doc__': None}
    {'x': 4, 'y': 5, 'z': 15}
    {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000000005F3BD90>, '__delattr__': <function Point.__delattr__ at 0x0000000005F3BE18>, '__doc__': None}

    可以阻止通过实例删除属性的操作。但是通过类依然可以删除属性。

    __getattribute__

    class Base:
        n = 0
        
    class Point(Base):
        z= 6
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __getattr__(self,item):
            return "missing {}".format(item)
        def __getattribute__(self,item):
            return item
        
    p1 = Point(4,5)
    print(p1.__dict__)
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    print(Point.__dict__)
    print(Point.z)
    
    结果为:
    __dict__
    x
    z
    n
    t
    {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000000005C3D620>, '__getattr__': <function Point.__getattr__ at 0x0000000005C3DEA0>, '__getattribute__': <function Point.__getattribute__ at 0x0000000005C3D510>, '__doc__': None}
    6

    实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个attributeerror异常。

    它的return值将作为属性查找的结果,如果抛出attributeerrror异常,则会直接调用__getattr__方法,因为表示属性没有找到。

    class Base:
        n = 0
        
    class Point(Base):
        z= 6
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __getattr__(self,item):
            return "missing {}".format(item)
        def __getattribute__(self,item):
            return object.__getattribute__(self,item)
        
    p1 = Point(4,5)
    print(p1.__dict__)
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    print(Point.__dict__)
    print(Point.z)
    
    结果为:
    {'x': 4, 'y': 5}
    4
    6
    0
    missing t
    {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000000005C3D400>, '__getattr__': <function Point.__getattr__ at 0x0000000005C3DE18>, '__getattribute__': <function Point.__getattribute__ at 0x0000000005C3DC80>, '__doc__': None}
    6

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

    总结:

     
     
  • 相关阅读:
    telnet连接ip
    完成端口————留着看
    mysql设置最大连接数量
    前端页面唯一字符串生成(Js)UUID
    java加密MD5实现及密码验证
    cookie中存取中文字符
    自定义标签——带标签体
    简单讨论数据类型(byte)强制转化后的数值变化规律
    般若波罗蜜多心经(转载)
    table 表头固定
  • 原文地址:https://www.cnblogs.com/xpc51/p/11793372.html
Copyright © 2011-2022 走看看