问题
问题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__中查找
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
增加实例属性,而是想对类属性操作怎么办呢。解决方案:
- 使用
class.attr
改变值;
Foo.a = 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同名的描述符对象...
**描述符十分强大。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.__se