#! /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__)
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(优先级低于本地赋值的变量)