zoukankan      html  css  js  c++  java
  • Python核心编程-描述符

    python中,什么描述符。描述符就是实现了"__get__”、“__set__”或”__delete__” 方法中至少一个的对象。什么是非数据描述符,就是实现了__get__方法的对象,也就是初始化后,就只能读。数据描述符就是实现了__get__和__set__方法的对象,也就是说这个属性可读可重新赋值。

    看一下Python核心编程中描述符的例子:

    class DevNull2(object):
        def __get__(self, obj, typ=None):
            print 'Accessing attribute... ignoring'
        def __set__(self, obj, val):
            print 'Attempt to assign %r... ignoring' % (val)
    
    class C2(object):
        foo = DevNull2()
    c2 = C2()
    c2.foo = 'bar'

    这里DevNull2就是一个描述符,还有,下面的c2.foo的赋值并不是将DevNull2这个替换,因为Devnull2是一个描述符,所以,这里相当于代替了foo的属性值,在进行赋值操作的时候,会走DevNull2中的__set__方法。为什么描述符可以代表对象属性也就是为什么访问属性时调用的是__set__,__get__方法也不是访问的属性。这里需要了解属性解析的执行方式,对于对象来说属性解析机制在object.__getattribute__()方法中,因为对每个属性的实例都会调用到这个特殊的方法。这个方法被用来查找类的属性,同时也是你的一个代理,调用它可以进行属性的访问等操作回顾一下上面的原型,如果一个实例调用了__get__()方法,这就可能传入了一个类型或类的对象。举例来说,给定类 X 和实例 x, x.foo 由__getattribute__()转化成:
    type(x).__dict__['foo'].__get__(x, type(x))

    如果类调用了__get__()方法,那么 None 将作为对象被传入(对于实例, 传入的是 self):
    X.__dict__['foo'].__get__(None, X)

    优先级的排序:

    • 类属性
    • 数据描述符
    • 实例属性
    • 非数据描述符
    • 默认为__getattr__()

    在优先级链中,类字典中发现的数据描述符的优先级高于实例变量,实例变量优先级高于非数据描述符,如果提供了getattr(),优先级链会为getattr()分配最低优先级。对于一个给定的对象类,可以通过自定义__getattribute__方法来重写优先级链。

    另外一个例子来说明怎么使用描述符

    class TypedProperty(object):  
        def __init__(self, key, typ, val=None):
            print 'key :' , key
            self.key = '_' + key
            print 'typ :' , typ
            self.typ = typ
            print 'val :' , val
            self.val = val if val else typ()
     
        def __get__(self, instance, cls):
            print 'in __get__     ', self.key, self.typ, self.val, instance
            return getattr(instance, self.key, self.val)
       
        def __set__(self, instance, value):
            print 'in __set__       ', self.key, self.typ, self.val, value
            if not isinstance(value, self.typ):
                raise TypeError("Must be a %s" % self.typ)
            setattr(instance, self.key, value)
      
        def __delete__(self, instance):
            print 'in __delete__'
            raise AttributeError("Can't delete attribute")
       
    class Foo(object):
        name = TypedProperty("name", str)
        num = TypedProperty("num", int, 42)
    foo = Foo()
    foo.num = 12
    foo.num
    结果:

    key : name
    typ : <type 'str'>
    val : None
    key : num
    typ : <type 'int'>
    val : 42
    in __set__ _num <type 'int'> 42 12
    in __get__ _num <type 'int'> 42 <__main__.Foo object at 0x7f371f94a310>

     

    都能看出来,在描述符中的key属性就是属性的名,val是属性值。肯定有有人注意到在__init__()方法中,将key值设成了‘_’ + key,这里并不是一定要写成这样,这里的目的主要就是为了在__set__和__get__方法中的instance也就是Foo的实例中的属性不要与描述符中的key的形式一样,就是如果instance中的属性是num,描述符中的属性就不能写成num,要区分开,这里在前边放了一个‘_’来区分(可以用任何形式来区分,也就是'_'可以随便写成‘whatever’)。如果没有区分开,在调用foo.num = 12 的时候,走进__set__方法中,在setattr()方法中又调用了instantce中的key属性也就是又调用了foo.num,最终形成一个循环。导致maximum recursion depth exceeded while getting the str of an object

    python提供更简介的方式(利用property()内建函数):

    property(fget=None, fset=None, fdel=None, doc=None)

    class HideX(object):
        def __init__(self, x):
            self.x = x
        def get_x(self):
            return ~self.__x
        def set_x(self, x):
            assert isinstance(x, int), '"x" must be an integer!'
            self.__x = ~x
        x = property(get_x, set_x)
    inst = HideX(20)
    print inst.x
    inst.x = 30
    print inst.x

    也可以用set_x和get_x方法设置和获取属性。如果利用property()方法感觉太乱,也可以用@property

    class HideX(object):
        def __init__(self, x):
            self.x = x
            @property
            def x():
                def fget(self):
                    return ~self.__x
                def fset(self, x):
                    assert isinstance(x, int), '"x" must be an integer!'
                    self.__x = ~x
                return locals()

    这时也就只能用inst.x来获取或者重新赋值了。

    还可以利用下面的方法:

    class C(object):
        def __init__(self):
            self._x = None
        @property
        def x(self):
            return self._x
        @x.setter
        def x(self, value):
            self._x = value
        @x.deleter
        def x(self):
            del self._x
  • 相关阅读:
    数据库设计
    构建评价
    Schema xds文献
    架构设计评价
    需求分析评价
    获取script的链接参数并执行
    js获取封装对象/通过id tag className
    通过css/js来固定div的位置
    nginx日志分析工具goaccesss
    如何快速安装 allure
  • 原文地址:https://www.cnblogs.com/badboyf/p/6137283.html
Copyright © 2011-2022 走看看