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
  • 相关阅读:
    Power BI 根据用户权限动态生成导航跳转目标
    Power BI Tooltips 增强功能
    Power BI refresh error “could not load file or assembly…provided impersonation level is invalid”
    SQL 错误代码 18456
    如何使用SQL Server Integration Services从多个Excel文件读取数据
    通过表格编辑器将现有表引入Power BI数据流
    Power BI 中动态增长的柱状图
    ambari2.7.3离线安装hdp3.1.0时,ambari-hdp-1.repo中baseurl无值
    ambari 安装 cannot download file mysql-connector-java from http://8080/resource/mysql-connector-java.jar
    洛谷P4180 [BJWC2010]严格次小生成树
  • 原文地址:https://www.cnblogs.com/badboyf/p/6137283.html
Copyright © 2011-2022 走看看