Python descriptor(描述符)的实现_python

来源:脚本之家  责任编辑:小易  

libancillary可以做到这点(困简州其实也是2113socket):http://www.normalesup.org/~george/comp/libancillary/不过它是C代码5261所以你需4102要些binding,比如ctypes:汪蔽http://www.python.net/crew/theller/ctypes/tutorial.html我想你都问的这么深了1653就不用我写示范了吧咐信,是不是要调用 dup()函数复制一下文件描述符?追问。。额我希望是在进程间。。不是进程内传递www.zgxue.com防采集请勿采集本网。

问题

问题1

Python是一种动态语言,不支持类型检查。当需要对一个对象执行类型检查时,可能会采用下面的方式:

关于@property装饰器 在Python中我们使用@property装饰器来把对函数的调用伪装成对属性的访问。 那么为什么要这样做呢?因为@property让我们将自定义的代码同变量的访问/设定联系在了一起,同时为你的类保持一个简单的访问属性的接口。 举个栗子

class Foo(object): def __init__(self,a): if isinstance(a,int): self.__a = a else: raise TypeError("Must be an int") def set_a(self,val): if isinstance(val,int): self.__a = val else: raise TypeError("Must be an int") def get_a(self): return self.__a

本地地质错误

上述是一种类型检查的方法,但是如果需要类型检查的参数非常多的话就会变得非常繁琐,重复代码太多,Python这么简洁,优雅,肯定有更好的解决方法。另外上述方法也有缺陷。

这个是函数,你要用fileno();而不是fileno

f = Foo(1)print f.get_a() #1print f._Foo__a #1,还是能访问到= =f._Foo__a = 'test'print f.get_a() #test,还是改变了__a的值,而且不是int型print f._Foo__a #test

是完全通配的意思,\s是指空白,包括空格、换行、tab缩进等所有的空白,而\S刚好相反 这样一正一反下来,就表示所有的字符,完全的,一字不漏的。 另外,[]这个符号,表示在它里面包含的单个字符不限顺序的出现,比如下面的正则: [ace]* 这表示

问题2

在一个对象中,创建一个只读属性。

《流畅的Python》这本书是图灵科技翻译出版的一本书,作者Luciano Ramalho。作者从Python的特性角度出发,以Python的数据模型和特殊方法为主线,主要介绍了python的集合类型(sequence、mapping、set等)、把函数作为一等对象来使用、类的构建、

问题3

class Foo(object): a = 1f = Foo()print f.a #1,实例属性中没有a属性,所以到Foo.__dict__中查找print Foo.a #1print f.__dict__ #{}f.a = 2 #增加一个名为a的实例属性print f.a #2,搜索属性时先在实例字典中查找,然后再去类的字典中查找,在实例的__dict__中找到了..print Foo.a #1print f.__dict__ #{'a': 2}

如果不想给实例f增加实例属性,而是想对类属性操作怎么办呢。解决方案:

1) 使用class.attr改变值;

Foo.a = 2就不会给实例增加实例属性了。

2) 自定义属性访问,描述符;

class descriptor(object): def __init__(self,val): self.val = val def __get__(self,obj,type = None): print 'get', return self.val def __set__(self,obj,val): print 'set',val self.val = val def __delete__(self,obj): raise AttributeError("Can't delete attribute")class Foo(object): a = descriptor(0)f = Foo()print f.a #get 0print Foo.a #get 0print f.__dict__ #{}f.a = 2 #set 2,并没有增加实例属性print f.a #get 2print Foo.a #get 2print f.__dict__ #{}

问题总结

上述三个问题均与属性访问有关,如果能够自定义属性访问,上述问题就能解决啦= =。其实问题3已经给出了解决方法,就是描述符…

描述符的定义和介绍

​ 描述符(Descriptor)是Python中非常重要的一部分,它广泛应用于Python的内核。

​ 一般来说,描述符就是一个带有绑定方法的对象,只不过照比其他对象多了几个特殊的描述符方法,即 __get__(),__set__(),__delete__()对描述符对象的属性访问主要通过描述符方法。

定义:一个对象如果定义了__get__(),__set__(),__delete__()方法中的任何一个,它就可以被称为描述符。

​ 对属性的访问默认是从对象的字典中获取(get),设置(set)和删除(delete)属性。

假设有实例a,a有属性x,获取a.x的值。

一般来说,a.x会按以下顺序查找属性,查找链: a.__dict__['x']type(a).__dict__['x']根据mro顺序在type(a)的父类中查找(不包括元类)

​ 但是,如果被查找的属性是一个描述符,并且为类属性,那么就会覆盖其默认行为,转而去调用描述符方法。注意,描述符仅适用于新式类和新式对象。

class descriptor(object): def __init__(self,val): self.val = val def __get__(self, obj, objtype): print 'get', return self.val def __set__(self, obj, val): print 'set' self.val = valclass Foo(object): x = descriptor(0) y = 0f = Foo()'''描述符覆盖默认的访问行为'''print f.x #get 0,调用的是描述符的__get__函数print f.__dict__ #{}f.x = 1 #set,调用的是描述符的__set__函数print f.x #get 1print f.__dict__ #{},即使赋值也没有增加实例属性...,是不是覆盖了默认行为- -f.y = 2 print f.__dict__ #{'y': 2},因为没有与y同名的描述符对象...

描述符十分强大。properties,methods, static methods,class methods, and super()都是基于描述符实现的。从Python2.2开始,描述符就在新式类中出现了。描述符是一个灵活的工具,使程序开发更加便利。感觉描述符很吊啊…

Descriptor Protocol(描述符协议)

descriptor.__get__(self, obj, type=None) --> value

descriptor.__set__(self, obj, value) --> None

descriptor.__delete__(self, obj) --> None

上述三个方法就是描述符方法,如果一个对象定义了描述符方法中的任何一个,那么这个对象就会成为描述符。

假设有一个对象t,t.a是一个定义了三个描述符方法的描述符,并且a是类属性,调用情况如下:

print t.a → a.__get__(t, type(t))

t.a = v → a.__set__(t, v)

del t.a → a.__delete__(t)

注意:当描述符作为类属性时,才会自动调用描述符方法,描述符作为实例属性时,不会自动调用描述符方法。

数据和非数据描述符

data descriptor(数据描述符):定义了__get__()__set__()方法的对象;

non-data descriptor(非数据描述符):定义了__get__()的对象;非数据描述符典型应用是class methods。

数据和非数据描述符的区别

如果一个实例的字典(__dict__)中有一个和数据描述符对象相同名字的实例属性,则优先访问数据描述符;

class descriptor(object): def __init__(self,val = 0): self.val = val def __get__(self, obj, objtype): print '__get__', return self.val def __set__(self, obj, val): self.val = valclass Foo(object): a = descriptor()#数据描述符f = Foo()f.a = 1 #不会增加实例属性,会调用descriptor.__set__方法,数据描述符的优先级比实例属性高print f.a #__get__ 1,调用的是descriptor.__get__方法print f.__dict__ #{}

如果一个实例中存在和数据描述符名字相同的实例属性,利用下述方法就可以访问实例属性…

f.__dict__['a'] = 2 #增加一个与数据描述符同名的实例属性print f.__dict__ #{'a': 2}print f.a #__get__ 1,正常使用,调用的仍是描述符的__get__方法print f.__dict__['a'] #2,这个时候就是实例属性了...

如果一个实例的字典中有一个和非数据描述符对象相同名字的实例属性,则优先访问实例属性;

class descriptor(object): def __init__(self,val = 0): self.val = val def __get__(self, obj, objtype): print '__get__', return self.valclass Foo(object): a = descriptor()#非数据描述符,没有__set__f = Foo()print f.a #__get__ 0,调用descriptor.__get__方法f.a = 1 #增加实例属性,因为实例属性的优先级比非数据描述符高print f.a #1,不会调用descriptor.__get__方法,实例属性的优先级比非数据描述符高print f.__dict___ #{'a': 1},增加了实例属性a

数据和非数据描述符测试code

class data_descriptor(object):#数据描述符 def __init__(self,val): self.val = val def __get__(self, obj,type = None): print 'data get' return self.val def __set__(self, obj, value): if not isinstance(value,int):#赋值时可以类型检查啊 raise ValueError('value must be int') self.val = valueclass non_data_descriptor(object):#非数据描述符 def __init__(self,val): self.val = val def __get__(self,obj,type = None): print 'non data get' return self.valclass Foo(object): data = data_descriptor(0) non_data = non_data_descriptor(1)f = Foo()print f.__dict__ #{}print f.data #data get,0print f.non_data #non data get,1f.data = 2 #数据描述符优先级较高,不会创建实例属性,而是调用描述符的__set__方法f.non_data = 3 #增加了与非数据描述符同名的实例属性print f.__dict__ #{'non_data': 3}print f.data #data get,2 调用数据描述符的__get__()方法print f.non_data #3,非数据描述符优先级比实例属性低print Foo.non_data #non data get 1,利用类属性查找还是可以访问非数据描述符的,非数据描述符值未改变

属性访问,__getattribute__()

​ 在Python中,__getattribute__()就是属性解析机制,当调用一个属性时,不管是成员还是方法,都会触发 __getattribute__()来调用属性。

属性解析机制按照优先级链搜索属性。在优先级链中,类字典中的数据描述符的优先级高于实例变量,实例属性的优先级高于非数据描述符,如果定义了__getattr()__,优先级链会为__getattr()__分配最低优先级。可以通过自定义__getattribute__方法来重写优先级链。

优先级链 : 数据描述符 > 实例属性 > (非数据描述符,非描述符的类属性) > __getattr()__

上述所说的描述符均要为类属性,当描述符作为实例属性出现时,不会自动调用描述符方法。

class Foo(object): def __init__(self): self.x = 1 self.y = 2 def __getattribute__(self,keys = None):#这样优先级链,描述符什么的就都没用了。。。。 return 'test'f = Foo()print f.x,f.y,f.z #test,test,test,优先级链什么的都没用啦= =

__getattribute__()的Python实现大致如下,

def __getattribute__(self, key): "Emulate type_getattro() in Objects/typeobject.c" v = object.__getattribute__(self, key) if hasattr(v, '__get__'):#如果v定义了__get__方法的话,优先调用v.__get__ return v.__get__(None, self) return v

调用描述符

​ 描述符调用的细节取决于obj是一个对象还是一个类。不管是哪种,描述符只对新式对象和新式类起作用。继承了 object 的类是新式类。

​ 描述符也是一个对象,可以通过方法名直接调用描述符方法,如描述符d,d.__get__(object)。另外,在访问描述符时会自动调用相应的描述符方法(只有为类属性时才会自动调用)。描述符的自动调用机制基于__getattribute__()__getattribute__()确保了descriptor的机制,所以,如果重写了__getattribute__, 就可以消除descriptor机制。

​ 对于对象来说,obj.__getattribute__()会将b.x转化为 type(b).__dict__['x'].__get__(b,type(b)) ,按照下述顺序搜索属性:

类属性中的数据描述符 > 实例变量 > 类属性的非数据描述符 > __getattr__()

​ 对于类来说,class.__getattribute__()会将B.x转化为B.__dict__['x'].__get__(None,B)

记住以下几点:

1.描述符的调用基于__getattribute__();

2.重写__getattribute__()会阻止描述符的正常调用;

3.object.__getattribute__()和type.__getattribute__()会调用不同的__get__();

4.数据描述符一直覆盖实例属性,即同时存在同名的数据描述符和实例属性,优先调用数据描述符

5.非数据描述符一直被实例属性覆盖,即同时存在同名的非数据描述符和实例属性,优先调用实例属性;

​ 描述符的机制在 object, type, 和 super__getattribute__()方法中实现。由 object 派生出的类自动继承这个机制,或者它们有个有类似机制的元类。如果想让描述符失效的话,可以重写 __getattribute__()

class descriptor(object): def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): print 'get', self.name, return self.val def __set__(self, obj, val): print 'set', self.name,val self.val = valclass Foo(object): x = descriptor(10, 'var "x"') y = 5m = Foo()'''访问m.x的三种方法'''print m.x #get var "x",10print type(m).__dict__['x'].__get__(m,type(m)) #m.x会转化为这种形式,等价于m.xprint m.__getattribute__('x') #等价于m.x,因为x定义了__get__方法,调用x的__get__方法,上面已经给出了__getattribute__的实现原理'''设置m.x的值'''m.x = 20 #set var "x" 20type(m).__dict__['x'].__set__(m,20) #m.x = 20会转化为此种形式,等价于m.x = 20print m.x #get var "x",20print m.y #5#print type(m).__dict__['y'].__get__(m,type(m)) #error,AttributeError: 'int' object has no attribute '__get__'print m.__getattribute__('y') #5,等价于m.y

描述符的陷阱

描述符应放在类层次上,即作为类属性(class level)

说了N多次了,要谨记…

class Foo(object): y = descriptor(0) def __init__(self): self.x = descriptor(1) #实例属性的描述符是不会自动调用对应的描述符方法的...b = Foo()print "X is %s, Y is %s" % (b.x, b.y)#X is <__main__.descriptor object at 0x10432c250>, Y is 0print "Y is %s"%b.y.__get__(b) #需要自己调用__get__方法,解释器不会自己调用的

从上述代码可知,

    访问类层次上的描述符会自动调用相应的描述符方法; 访问实例层次上的描述符只会返回描述符对象自身,并不会调用相应的描述符方法;

(实例属性的描述符不会自动调用描述符方法,这么做肯定是有原因的吧…有知道的大神求指导…)

**描述符是所有实例共享的,让不同实例保存的值互不影响

class descriptor(object): def __init__(self, default): self.value = default def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = valueclass Foo(object): bar = descriptor(5)

bar是类属性,所有Foo的实例共享bar。

f = Foo()g = Foo()print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 5 g.bar is 5f.bar = 10 #调用__set__函数print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 10 g.bar is 10

当实例修改了描述符以后,会影响到其他实例,有没有一种方法可以让实例之间互不影响呢?

数据字典法

​ 在descriptor中使用数据字典,由__get____set__的第一个参数来确定是哪个实例,使用实例作为字典的key,为每一个实例单独保存一份数据,修改代码如下…

from weakref import WeakKeyDictionaryclass descriptor(object): def __init__(self, default): self.default = default self.data = WeakKeyDictionary() def __get__(self, instance, owner):# instance = x,owner = type(x) # we get here when someone calls x.d, and d is a descriptor instance return self.data.get(instance, self.default) def __set__(self, instance, value): # we get here when someone calls x.d = val, and d is a descriptor instance self.data[instance] = valueclass Foo(object): bar = descriptor(5)f = Foo()g = Foo()print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 5 g.bar is 5print "Setting f.bar to 10"f.bar = 10print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar) ##f.bar is 10 g.bar is 5

上述方法虽然可行,但是存在缺陷。

descriptor使用了一个字典来保存不同实例的数据。一般来说是不会出现问题,但是如果实例为不可哈希对象,如list,上述方法就会出现问题,因为不可哈希对象不能作为键值。

标签法

说白了就是给实例增加一个与描述符同名的实例属性,利用该实例属性来保存该实例描述符的值,描述符相当于一个中间操作,描述符的__get__()返回实例属性,__set__也是对实例属性操作。

此方法的实现原理: 数据描述符的访问优先级比实例属性高…

还是见图和代码吧,代码最直观…

这里写图片描述

class descriptor(object): def __init__(self, label):#label为给实例增加的实例属性名 self.label = label def __get__(self, instance, owner): #dict.get(k[,d]) -> D[k] if k in D, else d. d defaults to None. return instance.__dict__.get(self.label) #获取与描述符同名的实例属性的值 def __set__(self, instance, value): #注意这里,要这么写,不能写instance.x = val这种形式,这样会形成自身的循环调用 instance.__dict__[self.label] = value #修改与描述符同名的实例属性的值 class Foo(list): x = descriptor('x') #注意这个初始化值为要给实例增加的实例属性名,要和描述符对象同名。 y = descriptor('y')f1 = Foo()f2 = Foo()print f1.__dict__ #{}print f1.x,f2.x,f1.y,f2.y#None None None None,此时尚未增加实例属性,需要调用__set__方法建立一个与描述符同名的实例属性#print Foo.__dict__f1.x = 1f1.y = 2f2.x = 3f2.y = 4 print f1.__dict__ #{'y': 2, 'x': 1} #增加了的实例属性print f1.x,f1.y,f2.x,f2.y #1 2 3 4

因为只有调用了__set__函数才会建立一个与描述符同名的实例属性,所以可以在__init__()函数中对描述符赋值。

class Foo(list): x = descriptor('x') y = descriptor('y') def __init__(self): self.x = 1 #调用的是描述符的__set__方法,与描述符同名的实例属性增加完毕.... self.y = 2f = Foo()print f.x,f.y

注意事项:

给描述符添加标签时,初始化值要和描述符的变量名相同,比如name = descriptor(‘name'),因为这个初始化值是给实例增加的实例属性名,必须要和描述符对象同名。

下面为错误示范,初始化值和描述符不同名的情况。

class descriptor(object): def __init__(self, label): self.label = label def __get__(self, instance, owner): return instance.__dict__.get(self.label) def __set__(self, instance, value): if not isinstance(value,int): raise ValueError('must be int') instance.__dict__[self.label] = value class Foo(object): x = descriptor('y') #应该改为Descriptor('x'),与实例同名 def __init__(self,val = 0): self.x = valf = Foo()print f.x #0f.y = 'a' #绕过了描述符的__set__方法...未进行类型检查,此时x为非法值啊,是不是很坑...print f.x #a

潜在坑…正常使用时也会带来坑。

class Foo(object): x = descriptor('x') def __init__(self,val = 0): self.x = valf = Foo()f.x = 'a'#ValueError: must be int

好像没毛病啊…接着往下看。

f.__dict__['x'] = 'a'print f.x #a,还是绕过了__set__方法,未进行类型检查,还是非法值啊...哈哈- -

小结

​ 查了很多资料,标签法用的较多,但是标签法也有一定的缺陷,目前没有找到更好的方法解决上述问题,如有更好的方法,求指导,谢谢…

描述符的应用…

​ 描述符也是类,可以为其增加方法。比如增加回调方法,描述符是一个用来回调的很好的手段。比如想要一个类的某个状态发生变化就立刻通知我们,可以自定义回调函数用来响应类中的状态变化。如以下代码,

from weakref import WeakKeyDictionaryclass CallbackProperty(object): def __init__(self, default=None): self.data = WeakKeyDictionary() self.default = default self.callbacks = WeakKeyDictionary() def __get__(self, instance, owner): if instance is None: return self return self.data.get(instance, self.default) def __set__(self, instance, value):#每次改变值的时候都会调用low_balance_warning函数 for callback in self.callbacks.get(instance, []): # alert callback function of new value callback(value) self.data[instance] = value def add_callback(self, instance, callback): """Add a new function to call everytime the descriptor within instance updates""" if instance not in self.callbacks: self.callbacks[instance] = [] #实例->[方法,] self.callbacks[instance].append(callback)class BankAccount(object): balance = CallbackProperty(0)def low_balance_warning(value): if value < 100: print "You are now poor" else: print 'You are now rich!!!'def check(value): print 'You have %s money, Good Luck!!!'%valueba = BankAccount()BankAccount.balance.add_callback(ba, low_balance_warning)BankAccount.balance.add_callback(ba, check)ba.balance = 5000 # You are now rich!!! You have 5000 money, Good Luck!!!print "Balance is %s" % ba.balance # Balance is 5000ba.balance = 99 # You are now poor You have 99 money, Good Luck!!!print "Balance is %s" % ba.balance # Balance is 99

有木有感觉很厉害…__set__()方法感觉就像一个监督人员,监视属性的一举一动。

​ 描述符还有其他用处,如格式检查,类型检查,设置只读变量等。设置一个只读变量的话,只要不让变量再赋值就好了,即调用__set__()函数时触发异常即可,这也是问题2的答案。

class descriptor(object): def __init__(self,val): self.val = val def __get__(self, obj,type = None): return self.val def __set__(self, obj, value): raise Exception('read only')class Foo(object): d = descriptor(1)d = Foo()print d.d #1d.d = 2 #触发异常,read only

参考网址

1.https://docs.python.org/2/howto/descriptor.html#definition-and-introduction

2.https://hg.python.org/cpython/file/2.7/Objects/object.c

3.http://svn.python.org/view/python/trunk/Objects/classobject.c?view=markup

4.http://www.geekfan.net/7862/

5.http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html

6.//www.zgxue.com/article/62987.htm

7.//www.zgxue.com/article/97741.htm

8.http://www.tuicool.com/articles/yYJbqun

到此这篇关于Python descriptor(描述符)的实现的文章就介绍到这了,更多相关Python descriptor内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网! 您可能感兴趣的文章:Python描述符descriptor使用原理解析Python 描述符(Descriptor)入门详解Python中的Descriptor描述符类Python黑魔法Descriptor描述符的实例解析Python中的Descriptor描述符学习教程Python 的描述符 descriptor详解解密Python中的描述符(descriptor)python的描述符(descriptor)、装饰器(property)造成的一个无限递归问题分享

在5261Python中,访问一个属性的优先级顺序4102按照如下顺序:1.类属性2.数据描述符16533.实例属性4.非数据描述符5.__getattr__()方法。这个方法的完整定义如下所示:[python] view plain copydef __getattr__(self,attr) :#attr是self的一个属性名 pass; 先来阐述下什么叫数据描述符。数据描述符是指实现了__get__,__set__,__del__方法的类属性(由于Python中,一切皆是对象,所以你不妨把所有的属性也看成是对象)PS:个人觉得这里最好把数据描述符等效于定义了__get__,__set__,__del__三个方法的接口。阐述下这三个方法:__get__的标准定义是__get__(self,obj,type=None),它非常接近于JavaBean的get第一个函数是调用它的实例,obj是指去访问属性所在的方法,最后一个type是一个可选参数,通常为None(这个有待于进一步的研究)例如给定类X和实例x,调用x.foo,等效于调用:type(x).__dict__["foo"].__get__(x,type(x))调用X.foo,等效于调用:type(x).__dict__["foo"].__get__(None,type(x))第二个函数__set__的标准定义是__set__(self,obj,val),它非常接近于JavaBean的set方法,其中最后一个参数是要赋予的值第三个函数__del__的标准定义是__del__(self,obj),它非常接近Java中Object的Finailize()方法,指Python在回收这个垃圾对象时所调用到的析构函数,只是这个函数永远不会抛出异常。因为这个对象已经没有引用指向它,抛出异常没有任何意义。接下来,我们来一一比较这些优先级.首先来看类属性[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class A(object): foo=3 print A.foo a=A() print a.foo a.foo=4 print a.foo print A.foo 上面这段代码的输出如下:3343从输出可以看到,当我们给a.foo赋值的时候,其实与类实例的那个foo是没有关系的。a.foo=4 这句话给a对象增加了一个属性叫foo。其值是4。最后两个语句明确的表明了,我们输出a.foo和A.foo的值,他们是不同的。但是为什么a=A()语句后面的print a.foo输出了3呢?这是因为根据搜索顺序找到了类属性。当我们执行a.foo=4的时候,我们让a对象的foo属性指向了4这个对象。但是并没有改变类属性foo的值。所以最后我们print A.foo的时候,又输出了3。[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class A(object): foo=3 a=A() a.foo=4 print a.foo del a.foo print a.foo 上面的代码,我给a.foo赋值为4,在输出一次之后就del了。两次输出,第一次输出的是a对象的属性。第二次是类属性。不是说类属性的优先级比实例属性的高吗。为啥第一次输出的是4而不是3呢?还是上面解释的原因。因为a.foo与类属性的foo只是重名而已。我们print a.foo的时候,a的foo指向的是4,所以输出了4。------------------------------------然后我们来看下数据描述符这一全新的语言概念。按照之前的定义,一个实现了__get__,__set__,__del__的类都统称为数据描述符。我们来看下一个简单的例子。[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __get__(self,obj,type=None): pass def __set__(self,obj,val): pass def __del__(self,obj): pass class A(object): foo=simpleDescriptor() print str(A.__dict__) print A.foo a=A() print a.foo a.foo=13 print a.foo 上面例子的输出结果如下:[plain] view plain copy{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x005511B0>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} None None None 从输出结果看出,print语句打印出来的都是None。这说明a.foo都没有被赋值内容。这是因为__get__函数的函数体什么工作都没有做。直接是pass。此时,想要访问foo,每次都没有返回内容,所以输出的内容就是None了。[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __get__(self,obj,type=None): return "hi there" def __set__(self,obj,val): pass def __del__(self,obj): pass class A(object): foo=simpleDescriptor() print str(A.__dict__) print A.foo a=A() print a.foo a.foo=13 print a.foo 把__get__函数实现以下,就可以得到如下输出结果:[plain] view plain copy{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x00671190>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} hi there hi there hi there 为了加深对数据描述符的理解,看如下例子:[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __init__(self): self.result = None; def __get__(self, obj, type=None) : return self.result - 10; def __set__(self, obj, val): self.result = val + 3; print self.result; def __del__(self, obj): pass class A(object): foo = simpleDescriptor(); a = A(); a.foo = 13; print a.foo; 上面代码的输出是166第一个16为我们在对a.foo赋值的时候,人为的将13加上3后作为foo的值,第二个6是我们在返回a.foo之前人为的将它减去了10。所以我们可以猜测,常规的Python类在定义get,set方法的时候,如果无特殊需求,直接给对应的属性赋值或直接返回该属性值。如果自己定义类,并且继承object类的话,这几个方法都不用定义。-----------------在这里看一个题外话。看代码[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __init__(self): self.result = None; def __get__(self, obj, type=None) : return self.result - 10; def __set__(self, obj, val): if isinstance(val,str): assert False,"int needed! but get str" self.result = val + 3; print self.result; def __del__(self, obj): pass class A(object): foo = simpleDescriptor(); a = A(); a.foo = "13"; print a.foo; 上面代码在__set__ 函数中检查了参数val,如果val是str类型的,那么要报错。这就实现了我们上一篇文章中要实现的,在给属性赋值的时候做类型检查的功能。-----------------------------------------------下面我们来看下实例属性和非数据描述符。[python] view plain copy# -*- coding:utf-8 -*- ''''' Created on 2013-3-29 @author: naughty ''' class B(object): foo = 1.3 b = B() print b.__dict__ b.bar = 13 print b.__dict__ print b.bar 上面代码输出结果如下: {}{'bar': 13}13那么什么是非数据描述符呢?简单的说,就是没有实现get,set,del三个方法的所有类。让我们任意看一个函数的描述:def call(): pass执行print dir(call)会得到如下结果:[plain] view plain copy['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name'] 先看下dir的帮助。dir列出给定对象的属性或者是从这个对象能够达到的对象。回到print dir(call)方法的输出,看到,call方法,有输出的那么多个属性。其中就包含了__get__函数。但是却没有__set__和__del__函数。所以所有的类成员函数都是非数据描述符。看一个实例数据掩盖非数据描述符的例子:[python] view plain copy''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __get__(self,obj,type=None) : return "get",self,obj,type class D(object): foo=simpleDescriptor() d=D() print d.foo d.foo=15 print d.foo 看输出:('get', <__main__.simpleDescriptor object at 0x02141190>, <__main__.D object at 0x025CAF50>, <class '__main__.D'>)15可见,实例数据掩盖了非数据描述符。如果改成数据描述符,那么就不会被覆盖了。看下面:[python] view plain copy''''' Created on 2013-3-29 @author: naughty ''' class simpleDescriptor(object): def __get__(self,obj,type=None) : return "get",self,obj,type def __set__(self,obj,type=None) : pass def __del__(self,obj,type=None) : pass class D(object): foo=simpleDescriptor() d=D() print d.foo d.foo=15 print d.foo 代码的输出如下:[plain] view plain copy('get', <__main__.simpleDescriptor object at 0x01DD1190>, <__main__.D object at 0x0257AF50>, <class '__main__.D'>) ('get', <__main__.simpleDescriptor object at 0x01DD1190>, <__main__.D object at 0x0257AF50>, <class '__main__.D'>) 由于是数据描述符,__set __函数体是pass,所以两次输出都是同样的内容。最后看下__getatrr__方法。它的标准定义是:__getattr__(self,attr),其中attr是属性名内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • 深入学习python多线程与gil
  • python numpy生成等差数列、等比数列的实例
  • 对pandas中to_dict的用法详解
  • 我们为什么要减少python中循环的使用
  • python 显示数组全部元素的方法
  • 15行python代码带你轻松理解令牌桶算法
  • 如何实现在jupyter notebook中播放视频(不停地展示图片)
  • python实现数据库并行读取和写入实例
  • python使用字典的嵌套功能详解
  • python爬取51job中hr的邮箱
  • python 函数是不是描述符
  • python 如何在进程间传递文件描述符?
  • 请问python描述符property中的self.fget(instance)...
  • python 描述符 作用
  • 如何正确地使用Python的属性和描述符
  • python连接Oracle报错:DatabaseError: ORA-12514:...
  • python中用fileno没返回文件描述符,请问是怎么回事?
  • 正则表达式中,[\s\S]* 什么意思。。。。“[ ]”不是...
  • 流畅的python适合入门吗
  • python连接Oracle报错:DatabaseError: ORA-12514:...
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全vbsdos/bathtahtcpythonperl游戏相关vba远程脚本coldfusionruby专题autoitseraphzonepowershelllinux shellluagolangerlang其它首页//www.jb51.net/article/62987.htm//www.jb51.net/article/97741.htmpython描述符descriptor使用原理解析python 描述符(descriptor)入门详解python中的descriptor描述符类python黑魔法descriptor描述符的实例解析python中的descriptor描述符学习教程python 的描述符 descriptor详解解密python中的描述符(descriptor)python的描述符(descriptor)、装饰器(property)造成的一个无限递归问题分享深入学习python多线程与gilpython numpy生成等差数列、等比数列的实例对pandas中to_dict的用法详解我们为什么要减少python中循环的使用python 显示数组全部元素的方法15行python代码带你轻松理解令牌桶算法如何实现在jupyter notebook中播放视频(不停地展示图片)python实现数据库并行读取和写入实例python使用字典的嵌套功能详解python爬取51job中hr的邮箱python入门教程 超详细1小时学会pycharm 2020最新永久激活码(附python 列表(list)操作方法详解python 元组(tuple)操作详解python 字典(dictionary)操作详解pycharm 使用心得(一)安装和首python strip()函数 介绍python 中文乱码问题深入分析python中使用xlrd、xlwt操作excepycharm 2020年最新激活码(亲测python imread、newaxis用法详解python3开发实例之非关系型图数据库neo4jpython3 串口接收与发送16进制数据包的实python3爬虫全国地址信息使用python进行体育竞技分析(预测球队成详解python用三种方式统计词频的方法bluepy 一款python封装的ble利器简单介绍python实现qq空间自动点赞功能python 循环语句之 while,for语句详解pytorch中的c++扩展实现
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved