zoukankan      html  css  js  c++  java
  • 面向对象知识点补充

    关于描述符(描述器)的理解

    先看段程序

    class Role:
        name = 'lxj'
        def run(self):
            print("running")
    
    r1 = Role()
    #在面向对象基础中我们已经知道__dict__是以字典的形式返回类或类实例的所有成员
    print(Role.__dict__,r1.__dict__)
    
    结果:{'__module__': '__main__', '__doc__': None, 'run': <function Role.run at 0x00260198>, '__weakref__': <attribute '__weakref__' of 'Role' objects>, '__dict__': <attribute '__dict__' of 'Role' objects>, 'name': 'lxj'} {}

    接着来了解下访问属性时的查找策略:比如当我们访问r1.name实际上先在自己(实例)的__dict__字典中找有没有name的key,如果没有在类中的__dict__字典找有没有name的key,如果没有并且类有继承则继续往上找

    class Role:
        name = 'lxj'
        def run(self):
            print("running")
    
    r1 = Role()
    
    #我们先来看下属性的访问
    print(Role.name,Role.__dict__['name'])
    #结果:lxj lxj
    
    #方法访问
    print(Role.run,Role.__dict__['run'])
    #结果:<function Role.run at 0x00620198> <function Role.run at 0x00620198>
    #结果也都是一样的,那想表达什么呢?其实在python2中Role.run是一个unbound method,看了很多资料都没了解python3中为什么不显示unbound方法了,希望有人指导
    
    #继续往下看
    print(r1.run)
    #结果:<bound method Role.run of <__main__.Role object at 0x0025D530>>
    

    总结:按照上面的查找策略,r1.run和Role.run应该是同一个方法才对,为什么会变成unbound method(暂且这么理解)和bound method
    可以理解为通过实例访问方法(r1.run),会得到bound method,通过类访问(Role.run)方法访问会得到unbound method

    为什么会有这个情况呢,因为descriptor(描述器)

    什么是descriptor?

    descriptor是对象的一个属性,只不过它存在于类的__dict__中并且有特殊方法__get__(可能还有__set__和__delete)而具有一点特别的功能,为了方便指代这样的属性,我们给它起了个名字叫descriptor属性

    descriptor特性

    descriptor必须依附对象,作为对象的一个属性,它不能单独存在。还有一点,descriptor必须存在于类的__dict__中

    只有在类的__dict__中找到属性,Python才会去看看它有没有__get__等方法,对一个在实例的__dict__中找到的属性,Python根本不理会它有没有__get__等方法,直接返回属性本身

    看代码演示

    class T():
        d = Descriptor()       #类中找到属性
        # def __init__(self):
        #     self.d = Descriptor()
    
    t = T()
    print(t.d)
    
    结果:
    ('get', <__main__.Descriptor object at 0x00246BF0>, <__main__.T object at 0x00246C70>, <class '__main__.T'>)
    
    
    class T():
        # d = Descriptor()
        def __init__(self):       #实例中找到属性
            self.d = Descriptor()
    
    t = T()
    print(t.d)
    结果:
    <__main__.Descriptor object at 0x005F6C70>
    没有调用__get__,验证了之前的论证
    

    __get__中参数的意义

    class Descriptor(object):
        def __get__(self, instance, owner):
            return 'get',self,instance,owner
    
    
    class T():
        d = Descriptor()
        # def __init__(self):
        #     self.d = Descriptor()
    
    t = T()
    print(t.d)  #等于d,__get__(t,T)
    print(T.d)  #等于d.__get__(None,T)
    结果:
    ('get', <__main__.Descriptor object at 0x005D6BF0>, <__main__.T object at 0x005D6C70>, <class '__main__.T'>)
    ('get', <__main__.Descriptor object at 0x005D6BF0>, None, <class '__main__.T'>)
    
    self即当前Descriptor的实例,这里即d,instance就是拥有它的对象,这里即t,owner是instance的类型,即T

    接下来说下__get__,__getattribute__,__getattr__区别

    __get__、__getattr__、__getattribute都是访问属性(这里我理解的是包括属性和方法)的方法

    引子
    假设我们有个类A,其中a是A的实例
    a.x时发生了什么?属性的lookup顺序如下:

    1. 如果重载了__getattribute__,则调用.
    2. a.__dict__, 实例中是不允许有descriptor的,所以不会遇到descriptor
    3. A.__dict__, 也即a.__class__.__dict__ .如果遇到了descriptor,优先调用descriptor.
    4. 沿着继承链搜索父类.搜索a.__class__.__bases__中的所有__dict__. 如果有多重继承且是菱形继承的情况,按MRO(Method Resolution Order)顺序搜索.
    class C(object):
        def __setattr__(self, name, value):
            print("__setattr__ called:", name, value)
            object.__setattr__(self, name, value)
    
        def __getattr__(self, name):
            print("__getattr__ called:", name)
    
        def __getattribute__(self, name):
            print("__getattribute__ called:", name)
            return object.__getattribute__(self, name)
        def func(self):
            print("aa")
    
    c = C()
    c.x = "foo"
    print(c.__dict__)
    print(c.x)
    
    结果:
    __setattr__ called: x foo
    __getattribute__ called: __dict__
    {'x': 'foo'}
    __getattribute__ called: x
    foo
    

      

    深入
    1.object.__getattr__(self, name)
    当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。

    2.object.__getattribute__(self, name)
    无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

    3.object.__get__(self, instance, owner)
    如果class定义了它,则这个class就可以称为descriptor。owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。(descriptor的实例自己访问自己是不会触发__get__,而会触发__call__,只有descriptor作为其它类的属性才有意义。)(所以下文的d是作为C2的一个属性被调用)

    class C(object):
        a = 'abc'
    
        def __getattribute__(self, *args, **kwargs):
            print("__getattribute__() is called")
            return object.__getattribute__(self, *args, **kwargs)
    
        def __getattr__(self, name):
            print("__getattr__() is called ")
            return name + " from getattr"
    
        def __get__(self, instance, owner):
            print("__get__() is called", instance, owner)
            return self
    
        def foo(self, x):
            print(x)
    
    class C2(object):
        d = C()
    
    if __name__ == '__main__':
        c = C()
        c2 = C2()
        print(c.a)
        print(c.zzzzzzzz)
        c2.d
        print(c2.d.a)
    
    结果:
    __getattribute__() is called
    abc
    __getattribute__() is called
    __getattr__() is called 
    zzzzzzzz from getattr
    __get__() is called <__main__.C2 object at 0x0032DB30> <class '__main__.C2'>
    __get__() is called <__main__.C2 object at 0x0032DB30> <class '__main__.C2'>
    __getattribute__() is called
    abc
    

  • 相关阅读:
    1.Netty 实战前言
    8.Netty发送对象
    7.Netty中 handler 的执行顺序
    6.高性能NIO框架netty
    5.NIO_ Selector选择器
    4.NIO_Channel 通道
    3.NIO_Buffer缓冲区
    2.Java NIO 简介
    那些堪称神器的 Chrome 插件
    获取当前操作的IFrame 对象的方法
  • 原文地址:https://www.cnblogs.com/zj-luxj/p/7259195.html
Copyright © 2011-2022 走看看