Python 高级编程和异步IO并发编程 --08_3 属性描述符与属性的查找过程

Githa ·
更新时间:2024-11-13
· 745 次阅读

Python类中属性查找过程:

from datetime import date,datetime import numbers class IntField: # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class User: age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.age = 18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.age) # 18 from datetime import date,datetime import numbers class IntField: # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") if value < 0: raise ValueError("positive value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class User: age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.age = -18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.age) # 18 Traceback (most recent call last): File "C:/Users/Amber/PycharmProjects/test0/Chapter08/attr_desc.py", line 28, in user.age = -18 # 此处给属性描述符赋值时,实际上会调用set方法 File "C:/Users/Amber/PycharmProjects/test0/Chapter08/attr_desc.py", line 13, in __set__ raise ValueError("positive value required") ValueError: positive value required

在属性描述符中,__get__/__set__/__delete__三个方法中的任意实现一个,就可以把它当作为属性描述符。

实际上,属性描述符包括两种,一:实现了__get__/__set__,数据描述符;二:非数据属性描述符,比如只实现get方法

属性查找过程:一般,先查找实例属性,然后查找类属性

from datetime import date,datetime import numbers class IntField: # 数据描述符 # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") if value < 0: raise ValueError("positive value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class NonDataIntField: # 非数据属性描述符 def __get__(self,instance,owner): pass class User: age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 ''' 如果user是某个类的实例,那么user.age(以及等价的getattr(user,'age'))首先调用__getattribute__. 如果定义了__getattr__方法,那么在__getattribute__抛出AttributeError的时候就会调用__getattr__ 而对于描述符(__get__)的调用,则是只发生在__getattribute__内部的。 user = User,那么user.age顺序如下: 1- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其__get__方法 《数据描述符的优先级最高》 2- 如果“age”是出现在obj(对象)的__dict__中,那么直接返回obj.__dict__['age'],否则 3- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其 - 3.1: 如果age是non-data descriptor,那么调用其__get__方法,否则 - 3.2: 返回__dict__['age'] 4- 如果User有__getattr__方法,调用__getattr__方法,否则(如果查找不到) 5- 抛出AttributeError ''' if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.age = 18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.__dict__) # {} print(user.age) # 18

上例,倒数第二句,print(user.__dict__)  输出为空字典,并没有出现在对象中,因为此时优先调用数据描述符,此时会优先进入IntField中的value中,并没有进入user这个实例中。

from datetime import date,datetime import numbers class IntField: # 数据描述符 # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") if value < 0: raise ValueError("positive value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class NonDataIntField: # 非数据属性描述符 def __get__(self,instance,owner): pass class User: # age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 age = NonDataIntField() ''' 如果user是某个类的实例,那么user.age(以及等价的getattr(user,'age'))首先调用__getattribute__. 如果定义了__getattr__方法,那么在__getattribute__抛出AttributeError的时候就会调用__getattr__ 而对于描述符(__get__)的调用,则是只发生在__getattribute__内部的。 user = User,那么user.age顺序如下: 1- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其__get__方法 《数据描述符的优先级最高》 2- 如果“age”是出现在obj(对象)的__dict__中,那么直接返回obj.__dict__['age'],否则 3- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其 - 3.1: 如果age是non-data descriptor,那么调用其__get__方法,否则 - 3.2: 返回__dict__['age'] 4- 如果User有__getattr__方法,调用__getattr__方法,否则(如果查找不到) 5- 抛出AttributeError ''' if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.age = 18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.__dict__) # {'age': 18} print(user.age) # 18

上例,第27行,注释掉 # age = IntField(),第30行,定义age = NonDataIntField(), 则倒数第二句,print(user.__dict__)  输出为{'age': 18},有出现在对象中

from datetime import date,datetime import numbers class IntField: # 数据描述符 # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") if value < 0: raise ValueError("positive value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class NonDataIntField: # 非数据属性描述符 def __get__(self,instance,owner): pass class User: age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 # age = NonDataIntField() ''' 如果user是某个类的实例,那么user.age(以及等价的getattr(user,'age'))首先调用__getattribute__. 如果定义了__getattr__方法,那么在__getattribute__抛出AttributeError的时候就会调用__getattr__ 而对于描述符(__get__)的调用,则是只发生在__getattribute__内部的。 user = User,那么user.age顺序如下: 1- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其__get__方法 《数据描述符的优先级最高》 2- 如果“age”是出现在obj(对象)的__dict__中,那么直接返回obj.__dict__['age'],否则 3- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其 - 3.1: 如果age是non-data descriptor,那么调用其__get__方法,否则 - 3.2: 返回__dict__['age'] 4- 如果User有__getattr__方法,调用__getattr__方法,否则(如果查找不到) 5- 抛出AttributeError ''' if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.__dict__["age"] = "abc" #user.age = 18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.__dict__) # {'age': 'abc'} print(user.age) # AttributeError: 'IntField' object has no attribute 'value'

如果第27行还是数据描述符,age = IntField(), 第30行注释掉# age = NonDataIntField(),新增59行user.__dict__["age"] = "abc",注释掉#user.age = 18,此时61行print(user.__dict__) 输出为{'age': 'abc'}。发现age在__dic__中

第62行,print(user.age) # AttributeError: 'IntField' object has no attribute 'value' 抛出异常。因为int类型并没有vale这个属性。

虽然59行,user.__dict__["age"] = "abc" user.age放在属性中,执行user.age时,还是按照顺序查找,抛出异常。除非采用如下方式:print(user.__dict__["age"]) # abc

from datetime import date,datetime import numbers class IntField: # 数据描述符 # 此处的类如何变成属性描述符?只需要实现get方法,或者是set方法,类里面的任一个方法 都是属性描述符,可以用于后续规则的检查 # IntField实现对Int类型的检查 def __get__(self,instance,owner): return self.value # __set__中定义了value参数,此处返回,需要确保前后一致,因此未self.value def __set__(self,instance,value): # 赋值/set的时候,进行了参数类型的检查 if not isinstance(value,numbers.Integral): raise ValueError("int value required") if value < 0: raise ValueError("positive value required") self.value = value # 将传进来的value赋给IntFiled这个class中 def __delete__(self,instance): pass class NonDataIntField: # 非数据属性描述符 def __get__(self,instance,owner): pass class User: age = IntField() # 实际数据库中,不会存储age这个字段,age通过自动计算获取;类实例化,传给age # age只要实现__get__/__set__/__delete__三个方法中的任一个方法,就是属性描述符 # 此处,age会调用IntField的__set__方法 # age = NonDataIntField() ''' 如果user是某个类的实例,那么user.age(以及等价的getattr(user,'age'))首先调用__getattribute__. 如果定义了__getattr__方法,那么在__getattribute__抛出AttributeError的时候就会调用__getattr__ 而对于描述符(__get__)的调用,则是只发生在__getattribute__内部的。 user = User,那么user.age顺序如下: 1- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其__get__方法 《数据描述符的优先级最高》 2- 如果“age”是出现在obj(对象)的__dict__中,那么直接返回obj.__dict__['age'],否则 3- 如果“age”是出现在User或其基类的__dict__中,且age是data descriptor, 那么调用其 - 3.1: 如果age是non-data descriptor,那么调用其__get__方法,否则 - 3.2: 返回__dict__['age'] 4- 如果User有__getattr__方法,调用__getattr__方法,否则(如果查找不到) 5- 抛出AttributeError ''' if __name__ == "__main__": user = User() # 通过属性描述符,实现了对赋值时候的行为的控制 # user.age = "abc" # 此处会报错,提示 raise ValueError("int value required"),ValueError: int value required user.__dict__["age"] = "abc" #user.age = 18 # 此处给属性描述符赋值时,实际上会调用set方法 print(user.__dict__) # {'age': 'abc'} print(user.__dict__["age"]) # abc

__dict__ 属性属于user这个对象。

使用user.__dict__实际上是查找的__dict__属性,而不是age属性。查找与对象user无关


作者:世界非世界,是名世界!



io 属性 异步io Python

需要 登录 后方可回复, 如果你还没有账号请 注册新账号