详解参见官方文档: https://docs.python.org/zh-cn/3/howto/descriptor.html
1. 如果一个类中定义了__get__(), set (), delete ()三个方法中的任意一个, 那么这个类就是一个描述器
2. 非数据描述器
class A :
def __get__ ( self, instance, owner) :
"""
:param instance: 调用方类的实例
:param owner: 调用方的类
"""
print ( self, instance, owner)
class C :
name = A( )
p = C( )
print ( p. name)
print ( C. name)
< __main__. A object at 0x000001E025B0B320 > < __main__. C object at 0x000001E025BDAA58 > < class '__main__.C' >
None
< __main__. A object at 0x000001E025B0B320 > None < class '__main__.C' >
None
3. 访问描述器对象的属性
class A :
def __init__ ( self) :
self. cls_name = 'zs'
def __get__ ( self, instance, owner) :
return self
class C :
name = A( )
p = C( )
print ( p. name. cls_name)
zs
4. 当实例属性覆盖同名的类属性指向的非数据描述器时
class A :
def __init__ ( self) :
self. cls_name = 'zs'
def __get__ ( self, instance, owner) :
return self
class C :
name = A( )
def __init__ ( self) :
self. name = 'ls'
p = C( )
print ( p. name)
print ( p. name. cls_name)
ls
AttributeError: 'str' object has no attribute 'cls_name'
5. 如果一个对象定义了__set__()或__delete__(),那么它就被认为是一个数据描述器。
class A :
def __init__ ( self) :
self. cls_name = 'zs'
def __get__ ( self, instance, owner) :
return self
def __set__ ( self, instance, value) :
pass
class C :
name = A( )
def __init__ ( self) :
self. name = 'ls'
p = C( )
print ( p. name)
print ( p. name. cls_name)
6. 数据描述器__set__()的作用: 给描述器添加属性
class A :
def __init__ ( self) :
self. cls_name = 'zs'
def __get__ ( self, instance, owner) :
return self
def __set__ ( self, instance, value) :
print ( '__set__()被调用了' )
self. age = value
print ( value)
class C :
name = A( )
p = C( )
print ( p. name. cls_name)
p. name = 18
7. 描述器的应用一
class A :
def __init__ ( self) :
self. cls_name = 'zs'
@property
def get_name ( self) :
return self. cls_name
a = A( )
print ( a. get_name)
通过属性的方法调用函数
8. 描述器应用二
class A :
def __init__ ( self) :
self. __name = 'lw'
def get_name ( self) :
return self. __name
def set_name ( self, value) :
self. __name = value
def delete_name ( self) :
del self. __name
name = property ( fget= get_name, fset= set_name, fdel= delete_name)
a = A( )
print ( a. name)
a. name = 'ls'
print ( a. name)
del a. name
print ( a. name)
class A :
def __init__ ( self) :
self. __name = 'ww'
@property
def name ( self) :
return self. __name
@name. setter
def name ( self, value) :
self. __name = value
@name. deleter
def name ( self) :
del self. __name
a = A( )
print ( a. name)
a. name = 'ls'
print ( a. name)
del a. name
print ( a. name)
9. 手动实现property函数
class myproperty ( object ) :
'''自定义一个property'''
def __init__ ( self, gfunc= None , sfunc= None , dfunc= None ) :
self. gfunc = gfunc
self. sfunc = sfunc
self. dfunc = dfunc
def __get__ ( self, instance, owner) :
return self. gfunc( instance)
def setter ( self, sfunc) :
self. sfunc = sfunc
return self
def __set__ ( self, instance, value) :
self. sfunc( instance, value)
def deleter ( self, dfunc) :
self. dfunc = dfunc
return self
def __delete__ ( self, instance) :
self. dfunc( instance)
class Zoo ( object ) :
'''动物园'''
def __init__ ( self) :
self. animal_list = [ '大象' , '长颈鹿' ]
def get_animal ( self) :
'''获取所有的动物'''
return self. animal_list
def add_animal ( self, value) :
'''添加动物'''
self. animal_list. append( value)
def pop_animal ( self) :
'''删除最后一个动物'''
self. animal_list. pop( )
animal = myproperty( gfunc= get_animal, sfunc= add_animal, dfunc= pop_animal)
zoo = Zoo( )
print ( zoo. animal)
zoo. animal = '猪猪'
print ( zoo. animal)
del zoo. animal
print ( zoo. animal)
10. 使用描述器来实现一级缓存的目的
class CachedProperty ( object ) :
"""缓存"""
def __init__ ( self, func) :
self. func = func
self. func_name = func. __name__
def __get__ ( self, instance, owner) :
result = instance. __dict__[ self. func_name] = self. func( instance)
return result
class Person ( object ) :
@CachedProperty
def students ( self) :
"""
计算过程非常复杂,同一个对象不论调用几次返回的结果都是一样的
"""
print ( '数据库查询结果' )
return '使用缓存结果'
p = Person( )
print ( p. students)
print ( p. students)
print ( p. students)
数据库查询结果
使用缓存结果
使用缓存结果
使用缓存结果
结论: 以上实现一级缓存是利用了非数据描述器的"自己有同名属性就用自己的(自己.__dict__优先级大于描述器)" , 我们在__get__( ) 方法中添加了原来没有的属性并把它记录了下来, 在第一次访问时, 我们自己没有, 会用描述器的, 这样实现了优化访问的目的