python descriptor

#! /usr/bin/python
class des(object):
     def __init__(self, value):
         self.value = value
         self.instance = {}
     def __get__(self, instance, owner):
         if instance in self.instance:
            return self.instance[instance]
         else:
            self.instance[instance] = None
            return None
     def __set__(self, instance, value):
         self.instance[instance] = value

class test(object):
     attr = des(0)
     def __init__(self, name, school):
         self.name = name
         self.school = school


if __name__ == '__main__':
   print test.attr
   print '*' * 6
   t = test("test1", "test2")
   print t.__dict__
   print t.attr
   t.attr = 14
   print t.attr
   print t.__dict__
   print '*' * 6
   t1 = test("test3", "test4")
   print t.__dict__
   print t1.attr
   t1.attr = 35
   print t1.attr
   print t1.__dict__
   print "*" * 6
   print test.attr

执行结果为:

None
******
{'school': 'test2', 'name': 'test1'}
None
14
{'school': 'test2', 'name': 'test1'}
******
{'school': 'test2', 'name': 'test1'}
None
35
{'school': 'test4', 'name': 'test3'}
******
None


我们可以看到,t和t1的__dict__里面没有attr这个属性,所以我们在查找实例t和t1的属性attr时,实例本身是不存在attr属性的,所以我们找到test类变量attr,既然时test的类变量,那么test和test的实例t、t1均可以访问这个实例属性。



关于python descriptor

1.简介

我们可以自定义python descriptor,同时python也有许多内置的descriptor,包括functions、property、static method、class method

2.定义与介绍

通常而言,descriptor是一个对象属性,但是其拥有“绑定”的方法。访问这些对象属性我们要遵循descriptor定义的规则。descriptor定义了__get__() __set__() __delete__()方法。如果某个对象定义了上述方法中的一个,那么它就是一个descriptor。其实可以说类中定义的所有的方法均为类的属性。

descriptor是method、property、static method、class method背后的魔法。

3.descriptor定义的方法

descr.__get__(self, obj, type=None) 此方法返回值为value。

descr.__set__(self, obj, value)此方法的返回值为None。

descr.__delete__(self, obj) 此方法的返回值为None。

如果一个对象定义了上述方法的任意一个,那么这个对象就可以称为descriptor。

如果一个对象定义了__get__()和__set__()方法,那么这个对象可以看作是一个data descriptor。如果一个对象只定义了__get__()方法,那么此对象可以称之为non-data descriptor(比如说class method、static method、instance method等)。

data descriptor的优先级要高于本地的同名属性。而本地的同名属性的优先级则要高于non-data descriptor。

如果需要定义一个只读的data descriptor,那么我们需要定义__get__() __set__()方法,但是在__set__方法中我们需药引发AttributeError异常。

一个例子如下:

class RevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """

    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val
        #return obj.x__, 返回实例自己的值
    def __set__(self, obj, val):
        print 'Updating', self.name
        self.val = val
        #obj.x__ = value, 这里为使用RevealAccess的类实例均有自己的实例属性值,RevealAccess仅仅作属性访问赋值控制
>>> class MyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5

>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5
这里有一个问题,MyClass类中的RevealAccess实例是类实例,我们的MyClass所有对象均需要访问这同一个类实例,我们对调用某个类实例对RevealAccess作出的更改,那么所有派生于MyClass的实例均受此影响。( 我们之前定义了类实例,当类实例重新对类属性赋值时,其实时定义了一个本实例内的一个同名属性,而且我们通过类实例访问这个同名属性时,本地属性优先级要高于类属性,那么我们获得了本地属性值,但是在descriptor中,data decsriptor的优先级则要高于同名的本地属性,此时无论我们对此属性进行赋值还是访问都是实用类定义的类属性)。 Properties, bound and unbound methods, static methods, and class methods都是基于descriptor的。

4.property

property就是一个内置的data descriptor。使用python实现的property的代码如下所示:

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)


4.functions & methods

Python’s object oriented features are built upon a function based environment. Using non-data descriptors, the two are merged seamlessly.

类的字典将类中定义的方法存储为函数。在类定义中,方法是用def或者lambda来定义。方法和普通函数一个差异是第一个参数是类实例对象。

为了支持方法的调用,类中存储的函数(方法)作为non-data descriptor使用__get__方法来判断第一个传入的参数是类实例还是类本身,并且判断是否是绑定方法。


5.staticmethod & classmethod

class StaticMethod(object):
 "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

 def __init__(self, f):
      self.f = f

 def __get__(self, obj, objtype=None):
      return self.f

使用@Staticmethod语法躺,等同于func=StaticMethod(func),当使用instance.func时其实是调用instance.func.__get__(instance, instance.__class__)


class ClassMethod(object):
     "Emulate PyClassMethod_Type() in Objects/funcobject.c"

     def __init__(self, f):
          self.f = f

     def __get__(self, obj, klass=None):
          if klass is None:
               klass = type(obj)
          def newfunc(*args):
               return self.f(klass, *args)
          return newfound
上两个classmethod和staticmethod python descriptor的实现的non-data descriptor( 优先级低于本地赋值的变量



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值