Python3基础-描述符

描述符的概念

描述符就是一个“绑定行为“的对象属性,在描述符协议中,它可以通过方法充写属性的访问。这些方法有get(),set(),delete().如果这些方法中任何一个被定义在一个对象中,这个对象就是一个描述符

属性:dict(每个对象均具备该属性)  ==字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}。

class People():
    country = 'China'
    def __init__(self,name,age):
        self.name = name
        self.age =age
p1 = People('susu',28)
print(People.__dict__)
print('='*100)
print(p1.__dict__)

以上代码执行结果如下:

{'__module__': '__main__', 'country': 'China', '__init__': <function People.__init__ at 0x032E9738>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
====================================================================================================
{'name': 'susu', 'age': 28}

实例p1 的属性并不包含country ,country 是属于类People

魔法方法:__get__(), __set__(), __delete__()

方法的原型为:
1. __get__(self, instance, owner)
2. __set__(self, instance, value)
3. __delete__(self, instance)

self : 描述符实例
instance:相当于例子中的实例book
value: 就是要赋予的值

==简单的代码==

class Desc():
    def __get__(self, instance, owner):
        print("__get__")
        print("self=",self)
        print("instance=",instance)
        print("owner=",owner)
        print("=="*50,"\n")

class People():
    x = Desc()

p1 = People()
p1.x

print(People.__dict__)
print('-'*100)
print(p1.__dict__)

"""
执行结果如下
__get__
self= <__main__.Desc object at 0x02C40130>
instance= <__main__.People object at 0x02C400F0>
owner= <class '__main__.People'>
==================================================================================================== 

{'__module__': '__main__', 'x': <__main__.Desc object at 0x02C40130>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
----------------------------------------------------------------------------------------------------
{}

"""

 

可以从上面的代码看到,实例化People后,调用对象p1访问其属性x,会自动调用类的Desc的__get__方法

1、self= <__main__.Desc object at 0x02C40130>
是Desc的实例对象,也是People的属性x
2、instance= <__main__.People object at 0x02C400F0>
是People的实例对象,则是实例化后变量p1
3、owner= <class '__main__.People'>
是Peoplede 这个类;因为Desc是包含在它的内部,所以People拥有所属权

描述符的对象 x 其实是类People的类属性,那么可不可以把它变成实例属性呢

class Desc():
    def __init__(self,key):
        self.key = key

    def __get__(self, instance, owner):
        print("__get__")
        print("self.key==",self.key)

    def __set__(self, instance, value):
        print("__set__")

    def __delete__(self, instance):
        print("__delete__")
        instance.__dict__.pop(self.key)  #删除


class People():
     x = Desc('x')
     def __init__(self):
         self.age =Desc('age')

p1 = People()
print(p1.x)
print(p1.age)
print(p1.__dict__)
print(People.__dict__)

以上代码执行结果如下:

__get__
self.key== x
None
<__main__.Desc object at 0x012F09B0>
{'age': <__main__.Desc object at 0x012F09B0>}
{'__module__': '__main__', 'x': <__main__.Desc object at 0x012F00F0>, '__init__': <function People.__init__ at 0x013197C8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

如果是实例属性,则没有调用__get__方法。因为调用p1.age时,首先会去调用People的 __getattribute__() 方法。而age是一个描述符类,所以解释器将该方法转化为TestDesc.__dict__[‘age’].__get__(p1, People), 但是People 并没有 age这个属性,age 是属于实例对象的,所以,只能忽略了。

如果 类属性的描述符对象 和 实例属性描述符的对象 同名时

class Desc():
    def __init__(self,key):
        self.key = key

    def __get__(self, instance, owner):
        print("__get__")
        print("get里面的self",self)
        print("get里面的instance",instance)
        print("get里面的owner",owner)
        print("self.key==",self.key)
        print("=="*50,"\n")
        return  self.key

    def __set__(self, instance, value):
        print("__set__")
        print("set里面的self",self)
        print("set里面的instance",instance)
        print("set里面的owner",value)
       # instance.__dict__[self.key]=value


class People():
    name = Desc('name')
    #age  = Desc('age')
    def __init__(self,name,age):
        self.name = name
        #self.age = age

p1 = People('susu',200)
print(p1.name)
print(p1.__dict__)
"""
执行结果如下:
__set__
set里面的self <__main__.Desc object at 0x031909D0>
set里面的instance <__main__.People object at 0x031909F0>
set里面的owner susu
__get__
get里面的self <__main__.Desc object at 0x031909D0>
get里面的instance <__main__.People object at 0x031909F0>
get里面的owner <class '__main__.People'>
self.key== name
==================================================================================================== 

name
{}
"""
每次属性查找,这个协议的方法实际上是由对象的特殊方法getattribute()调用。每次通过点号(ins.attribute)或者getattr(ins, ‘attribute’)
函数调用都会隐式的调用getattribute(),它的属性查找的顺序如下:
1. 验证属性是否为实例的类对象的数据描述符 2. 查看该属性是否能在实例对象的dict中找到 3. 查看该属性是否为实例的类对象的非数据描述符
class Desc():
    def __init__(self,name):
        self.name = name

    def __get__(self, instance, owner):
        return  self.name

    def __set__(self, instance, value):
        print('instance==', instance)
        print('value==', value)

class People():
    _x = Desc('name')
    def __init__(self,x):
        self._x = x

p1 = People(10)
print(p1.__dict__)  #p1输出为空 {}
class Desc():def __get__(self, instance, owner):
        print("get")def __set__(self, instance, value):
        # print('self==',self)
        print('instance==', instance)
        print('value==', value)
        instance.__dict__['_x'] = value #固定写死的 

class People():
    _x = Desc('_x')
    def __init__(self,x):
        self._x = x

p1 = People(10)
print(p1.__dict__) #  {'_x': 10}

只要是内部定义了方法get,set,delete中的一个或多个,就可以称为描述符。如果实现了set和get,则称为数据描述符;如果只实现了get,则称为非数据描述符。

对象属性的访问顺序: 类属性>数据描述符>实例属性>非数据描述符>找不到的属性触发__getattr__()

class Desc():
    def __init__(self,name):
        self.name = name

    def __get__(self, instance, owner):
        print("__get__")
        # print("name==",self.name)
        # print("=="*50,"\n")
        return  self.name

    def __set__(self, instance, value):
        # print('self==',self)
        print('instance==', instance)
        print('value==', value)
        instance.__dict__['_x'] = value

class People():
    x = Desc('susu')  #x 是一个数据描述符

p1 = People()
p1.x  #触发了 __get__方法

People.x = 10 #重新设置类属性
p1.x  #就没有调用了 __get__方法
print(p1.x) #覆盖了数据描述符
类属性>数据描述符
class Desc():
    def __init__(self,name):
        self.name = name

    def __get__(self, instance, owner):
        print("__get__")
        # print("name==",self.name)
        # print("=="*50,"\n")
        return  self.name

    def __set__(self, instance, value):
        # print('self==',self)
        print('instance==', instance)
        print('value==', value)
        instance.__dict__['_x'] = value

class People():
    x = Desc('susu')  #x 是一个数据描述符

p1 = People()
p1.x =1
print(p1.x) #调用__get__方法  
数据描述符>实例属性
class Desc():
    def __init__(self,name):
        self.name = name

    def __get__(self, instance, owner):
        print("__get__")
        # print("name==",self.name)
        # print("=="*50,"\n")
        return  self.name

class People():
    x = Desc('susu')  #x 是一个数据描述符

p1 = People()
p1.x =1
print(p1.x) #返回的是 1
实例属性>非数据描述符
class Desc():
    def __init__(self,name):
        self.name = name

    def __get__(self, instance, owner):
        print("__get__")
        # print("name==",self.name)
        # print("=="*50,"\n")
        return  self.name


class People():
    x = Desc('susu')  #x 是一个数据描述符
    def __getattr__(self, item):
        print('找不到')

p1 = People()
p1.x # 会调用get方法
p1.y # 找不到才调用__getattr__方法
"""
执行结果:
__get__
找不到
"""
非数据描述符>找不到

 ========================描述符应用========================

class Desc():
    def __init__(self,key):
        self.key = key

    def __get__(self, instance, owner):
        print("__get__")return  instance.__dict__[self.key]  #获取

    def __set__(self, instance, value):
        print("__set__")
        instance.__dict__[self.key]=value  #写入

    def __delete__(self, instance):
        print("__delete__")
        instance.__dict__.pop(self.key)  #删除

class People():
    name = Desc('name')
    age  = Desc('age')
    def __init__(self,name,age):
        self.name = name
        self.age = age

p1 = People('susu',200)
print(p1.name)
print(p1.age)
print(p1.__dict__)
del p1.age
print(p1.__dict__)

以上代码执行结果如下:

__set__
__set__
__get__
susu
__get__
200
{'name': 'susu', 'age': 200}
__delete__
{'name': 'susu'}

为什么要用描述符

#name 输入的类型是str类型,如果不是str类型就报错
#age 输入的类型是int类型,如果不是int类型就报错
class Desc():
    def __init__(self,key,expected_type):
        self.key = key
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print("__get__")
        return  instance.__dict__[self.key]  #获取

    def __set__(self, instance, value):
        print("__set__")
        if not isinstance(value,self.expected_type):
            raise TypeError("%s 类型必须输入%s类型"%(self.key,self.expected_type))
        instance.__dict__[self.key]=value  #写入

    def __delete__(self, instance):
        print("__delete__")
        instance.__dict__.pop(self.key)


class People():
     name = Desc('name',str)
     age = Desc('age',int)
     def __init__(self,name,age,salary):
         self.name = name
         self.age = age
         self.salary = salary

p1 = People('susu',28,75222)
print(p1.name)
print(p1.age)
print(p1.__dict__)
print(People.__dict__)

 

 

 

 

 

posted @ 2019-11-22 16:58  槑槑DE  阅读(248)  评论(0编辑  收藏  举报