美文网首页PYTHON进阶
8.魔术方法(3)

8.魔术方法(3)

作者: Stone_説 | 来源:发表于2021-01-10 19:28 被阅读0次

    目录:
    1.反射概述
    2.反射相关的函数和方法
    3.发射的魔术方法

    1.反射概述

    运行时,区别于编译时,指的是程序被加载到内存中执行的时候。 反射,reflection,指的是运行时获取类型定义信息。
    一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type,class,attribute或method的能力,称为反射或者自省。
    具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等

    2.反射相关的函数和方法

    例1:

    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)
    Point(4,5)
    
    print(p.__dict__)
    {'x': 4, 'y': 5}
    
    p.__dict__['y'] = 16
    print(p.__dict__)
    {'x': 4, 'y': 16}
    
    p.z = 10
    print(p.__dict__)
    {'x': 4, 'y': 16, 'z': 10}
    
    print(dir(p))
    [
    '__class__', 
    '__delattr__', 
    '__dict__', 
    ...
    'show', 
    'x', 
    'y', 
    'z'
    ]
    
    print(p.__dir__())
    [
    'x', 
    'y', 
    'z', 
    '__module__', 
    ...
    '__class__'
    ]
    

    例2:
    例1通过属性字典dict来访问对象的属性,本质上也是利用的反射的能力。但是,访问方式不友好,Python提供了内置的函数。

    1.getattr(object,name[,default])
      通过name返回object的属性值,当属性不存在时,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串。
    2.setattr(object,name,value)
      object的属性存在,则覆盖,不存在,新增
    3.hasattr(object,name)
      判断对象是否有这个名字的属性,name必须为字符串
    
    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)
    
    p1 = Point(4,5)
    p2 = Point(10,10)
    print(repr(p1),repr(p2),sep='\n')
        <__main__.Point object at 0x0000012DFFBE6048>
        <__main__.Point object at 0x0000012DFFBE6080>
    
    print(p1.__dict__)
    setattr(p1,'y',16)
    setattr(p1,'z',10)
    print(getattr(p1,'__dict__'))
        {'x': 4, 'y': 5}
        {'x': 4, 'y': 16, 'z': 10}
    
    # 动态调用方法
    if hasattr(p1,'show'):
        getattr(p1,'show')()
    4 16
    
    # 动态增加方法
    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))
        <function <lambda> at 0x0000029957622EA0>
        <bound method <lambda> of <__main__.Point object at 0x00000299578050F0>>
        Point(14,26)
    
    # 为实例增加方法,未绑定
    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)
        Point(0,0)
        <function <lambda> at 0x0000024E87450BF8>
    
    # add在谁里面,sub在谁里面
    print(p1.__dict__)
        {
        'x': 4, 
        'y': 16, 
        'z': 10, 
        'sub': <function <lambda> at 0x0000024E87450BF8>
        }
    print(Point.__dict__)
        {
        '__module__': '__main__', 
        '__init__': <function Point.__init__ at 0x0000024E87450A60>, 
        '__str__': <function Point.__str__ at 0x0000024E87450AE8>, 
        'show': <function Point.show at 0x0000024E87450B70>, 
        '__dict__': <attribute '__dict__' of 'Point' objects>, 
        '__weakref__': <attribute '__weakref__' of 'Point' objects>, 
        '__doc__': None, 
        'add': <function <lambda> at 0x0000024E87272EA0>
        }
    

    3.反射相关的魔术方法

     魔术方法                                        含义
    __getattr__                         当通过搜索实例、实例的类及祖先类查不到的属性,就会调用此方法
    __setattr__                         通过.访问实例属性,进行增加、修改都要调用它
    __delattr__                         当通过实例来删除属性时调用此方法
    __getattribute__                    实例所有的属性调用都从这个方法开始
    
    3.1 __getattr__

    例1:

    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异常表示找不到属性
    查找啊属性顺序为:
    instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__--找不到-->调用__getattr__()
    
    3.2 __setattr__

    例1:

    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)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    p1.x = 50
    print(p1.x)
    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
    missing x
    {}
    {'x': 60}
    60
    
    # 实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成
    

    例2:

    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))
            self.__dict__[key] = value
    
    p1 = Point(4,5)
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    p1.x = 50
    print(p1.x)
    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
    50
    {'x': 50, 'y': 5}
    {'x': 60, 'y': 5}
    60
    
    #__setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__
    

    例3:
    __getattr__和__setattr__综合使用

    class B:
        b = 200
    
    class A(B):
        z = 100
        d = {}
        def __init__(self,x,y):
            self.x = x
            setattr(self,'y',y)
            self.__dict__['a'] = 5
    
        def __getattr__(self, item):
            print('~~~~~~~~~~~~',item)
            return self.d[item]
    
        def __setattr__(self, key, value):
            print(key)
            print(value)
            self.d[key] = value
    
        def __delattr__(self, item):
            print('can not del {}'.format(item))
    
    a = A(4,5)
    print(a.__dict__)
    print(A.__dict__)
    print(a.x,a.y)
    print(a.a)
    
    # 运行结果
    x
    4
    y
    5
    {'a': 5}
    {
    '__module__': '__main__', 
    'z': 100, 
    'd': {'x': 4, 'y': 5}, 
    '__init__': <function A.__init__ at 0x0000027293970A60>, 
    '__getattr__': <function A.__getattr__ at 0x0000027293970AE8>, 
    '__setattr__': <function A.__setattr__ at 0x0000027293970B70>, 
    '__delattr__': <function A.__delattr__ at 0x0000027293970BF8>, 
    '__doc__': None
    }
    ~~~~~~~~~~~~ x
    ~~~~~~~~~~~~ y
    4 5
    5
    
    3.3 __delattr__

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

    class Point:
        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 = 5
    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 0x0000025D783B0A60>, 
    '__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>, 
    '__dict__': <attribute '__dict__' of 'Point' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Point' objects>, 
    '__doc__': None
    }
    {'x': 4, 'y': 5, 'z': 5}
    {
    '__module__': '__main__', 
    '__init__': <function Point.__init__ at 0x0000025D783B0A60>, 
    '__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>, 
    '__dict__': <attribute '__dict__' of 'Point' objects>,
    '__weakref__': <attribute '__weakref__' of 'Point' objects>, 
    '__doc__': None
    }
    
    3.4 __getattribute__

    例1:

    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,p1.z,p1.n,p1.t)
    print(Point.__dict__)
    print(Point.z)
    
    # 运行结果
    __dict__
    x z n t
    {
    '__module__': '__main__',
    'z': 6, 
    '__init__': <function Point.__init__ at 0x000001F41B960AE8>, 
    '__getattr__': <function Point.__getattr__ at 0x000001F41B960B70>, 
    '__getattribute__': <function Point.__getattribute__ at 0x000001F41B960BF8>, 
    '__doc__': None
    }
    6
    
    # 实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。
    它的return值将作为属性查找的结果
    如果抛出AttributeError异常,则会直接调用__getattr__方法,因为表示属性没有找到
    

    例2:

    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,p1.z,p1.n,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 0x000001C13E300AE8>, 
    '__getattr__': <function Point.__getattr__ at 0x000001C13E300B70>, 
    '__getattribute__': <function Point.__getattribute__ at 0x000001C13E300BF8>, 
    '__doc__': None
    }
    6
    
    # __getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如:object.__getattribute__(self,name)
    注意:除非明确__getattribute__方法用来干什么,否则不要使用它
    

    相关文章

      网友评论

        本文标题:8.魔术方法(3)

        本文链接:https://www.haomeiwen.com/subject/tiqlaktx.html