问题
问题1
Python是一种动态语言,不支持类型检查。当需要对一个对象执行类型检查时,可能会采用下面的方式:
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这么简洁,优雅,肯定有更好的解决方法。另外上述方法也有缺陷。
f = Foo(1) print f.get_a() #1 print f._Foo__a #1,还是能访问到= = f._Foo__a = 'test' print f.get_a() #test,还是改变了__a的值,而且不是int型 print f._Foo__a #test
问题2
在一个对象中,创建一个只读属性。
问题3
class Foo(object): a = 1 f = Foo() print f.a #1,实例属性中没有a属性,所以到Foo.__dict__中<em>本文来源[email protected]搞@^&代*@码2网</em>查找 print Foo.a #1 print f.__dict__ #{} f.a = 2 #增加一个名为a的实例属性 print f.a #2,搜索属性时先在实例字典中查找,然后再去类的字典中查找,在实例的__dict__中找到了.. print Foo.a #1 print 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 0 print Foo.a #get 0 print f.__dict__ #{} f.a = 2 #set 2,并没有增加实例属性 print f.a #get 2 print Foo.a #get 2 print 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 = val class Foo(object): x = descriptor(0) y = 0 f = Foo() '''描述符覆盖默认的访问行为''' print f.x #get 0,调用的是描述符的__get__函数 print f.__dict__ #{} f.x = 1 #set,调用的是描述符的__set__函数 print f.x #get 1 print f.__dict__ #{},即使赋值也没有增加实例属性...,是不是覆盖了默认行为- - f.y = 2 print f.__dict__ #{'y': 2},因为没有与y同名的描述符对象...