网站建设站点无法发布,如何做学校网站app,常州手机网站建设,wordpress调用php文件文章目录1. 描述符示例#xff1a;验证属性2. 自动获取储存属性的名称3. 继承改进4. 覆盖型与非覆盖型描述符对比4.1 覆盖型描述符4.2 没有 __get__ 方法的覆盖型描述符4.3 非覆盖型描述符4.4 在类中覆盖描述符5. 描述符用法建议learn from 《流畅的python》
1. 描述符示例验证属性2. 自动获取储存属性的名称3. 继承改进4. 覆盖型与非覆盖型描述符对比4.1 覆盖型描述符4.2 没有 __get__ 方法的覆盖型描述符4.3 非覆盖型描述符4.4 在类中覆盖描述符5. 描述符用法建议learn from 《流畅的python》
1. 描述符示例验证属性
描述符是对多个属性 运用 相同存取逻辑的一种方式描述符是实现了 特定协议 的类这个协议包括 __get__、__set__ 和 __delete__ 方法 property 类实现了完整的描述符协议
实现了 __get__、__set__ 或 __delete__ 方法的类是描述符。描述符 的用法是创建一个实例作为另一个类的类属性 class Quantity:def __init__(self, storage_name):self.storage_name storage_name# storage_name 属性# 这是托管实例中存储值的 属性的名称def __set__(self, instance, value):# self 是描述符 实例即 LineItem.weight 或 LineItem.price# instance 是 托管实例LineItem 实例if value 0:instance.__dict__[self.storage_name] value# 必须直接 处理托管实例的 __dict__ 属性# 如果使用内置的 setattr 函数# 会再次触发 __set__ 方法导致无限递归# self.__dict__[self.storage_name] value 错误写法#else:raise ValueError(value must be greater than 0)class LineItem:weight Quantity(weight)# 描述符实例绑定给 weight 属性 类属性所有 LineItem实例共享price Quantity(price)def __init__(self, description, weight, price):self.description descriptionself.weight weightself.price pricedef subtotal(self):return self.weight * self.price为了避免 price Quantity(weight) 这样的错误 采用如下改进
2. 自动获取储存属性的名称
class Quantity:__counter 0 # Quantity 类属性统计实例数量def __init__(self):cls self.__class__ # cls 是 Quantity 类的引用prefix cls.__name__ # Quantityindex cls.__counterself.storage_name _{}#{}.format(prefix, index)# 每个描述符的 名称是独一无二的# 这种非法命名# 内置的 getattr 和 setattr 可以接受cls.__counter 1# storage_name 属性# 这是托管实例中存储值的 属性的名称def __set__(self, instance, value):# self 是描述符 实例即 LineItem.weight 或 LineItem.price# instance 是 托管实例LineItem 实例if value 0:setattr(instance, self.storage_name, value)else:raise ValueError(value must be greater than 0)def __get__(self, instance, owner):# owner 参数是托管类如 LineItem的引用# 通过描述符从托管类中获取属性时用得到return getattr(instance, self.storage_name)class LineItem:weight Quantity() # _Quantity#0# 描述符实例绑定给 weight 属性 类属性所有 LineItem实例共享price Quantity() # _Quantity#1def __init__(self, description, weight, price):self.description descriptionself.weight weightself.price pricedef subtotal(self):return self.weight * self.pricecoconuts LineItem(Brazilian coconut, 20, 17.95)
print(coconuts.weight, coconuts.price) # 20, 17.95
print(getattr(coconuts, _Quantity#0), getattr(coconuts, _Quantity#1))
# 20, 17.95print(LineItem.weight)
# 这种调用方式
# 描述符的 __get__ 方法接收到的 instance 参数值是 None
# AttributeError: NoneType object has no attribute _Quantity#0但是上面的报错信息让人困惑如何修改最好让 __get__ 方法返回描述符实例
def __get__(self, instance, owner):if instance is None:return selfreturn getattr(instance, self.storage_name)print(LineItem.weight)
# __main__.Quantity object at 0x000001DCA2683D003. 继承改进 import abcclass AutoStorge:__counter 0 # 统计实例数量def __init__(self):cls self.__class__ # cls 是 Quantity 类的引用prefix cls.__name__ # NonBlank, Quantityindex cls.__counterself.storage_name _{}#{}.format(prefix, index)# 每个描述符的 名称是独一无二的# 这种非法命名# 内置的 getattr 和 setattr 可以接受cls.__counter 1# storage_name 属性# 这是托管实例中存储值的 属性的名称def __set__(self, instance, value):# self 是描述符 实例即 LineItem.weight 或 LineItem.price# instance 是 托管实例LineItem 实例setattr(instance, self.storage_name, value) # 没有验证交给Validateddef __get__(self, instance, owner):if instance is None:return selfreturn getattr(instance, self.storage_name)class Validated(abc.ABC, AutoStorge):def __set__(self, instance, value):value self.validate(instance, value)# __set__ 方法把验证操作委托给 validate 方法super().__set__(instance, value)# 然后把返回的 value 传给超类的 __set__ 方法存储值abc.abstractmethod # 在这个类中validate 是抽象方法def validate(self, instance, value):to doclass Quantity(Validated): # 继承自 Validated 类def validate(self, instance, value): # 检查非正数if value 0:raise ValueError(value should be positive)return valueclass NonBlank(Validated): # 继承自 Validated 类a string with at least one non-space characterdef validate(self, instance, value):value value.strip() # 去除首尾空白if len(value) 0:raise ValueError(value cannot be empty or blank)return valueclass LineItem:description NonBlank() # _NonBlank#0weight Quantity() # _Quantity#0# 描述符实例绑定给 weight 属性 类属性所有 LineItem实例共享price Quantity() # _Quantity#1def __init__(self, description, weight, price):self.description descriptionself.weight weightself.price pricedef subtotal(self):return self.weight * self.pricecoconuts LineItem( Brazilian coconut , 20, 17.95)
print(coconuts.weight, coconuts.price) # 20 17.95
print(getattr(coconuts, _NonBlank#0)) #Brazilian coconut
print(getattr(coconuts, _Quantity#0), getattr(coconuts, _Quantity#1))
# 20 17.95
print(LineItem.weight) # __main__.Quantity object at 0x0000012B2A9AF9A0描述符的典型用途——管理 数据属性 这种描述符也叫覆盖型描述符因为描述符的 __set__ 方法使用托管实例中的同名属性覆盖即插手接管了要设置的属性
4. 覆盖型与非覆盖型描述符对比 通过实例读取属性时 通常返回的是实例中定义的属性 但是如果实例中没有指定的属性 那么会获取类属性。 而为实例中的属性赋值时通常会在实例中创建属性根本不影响类 ### 辅助函数仅用于显示 ###
#
def cls_name(obj_or_cls):cls type(obj_or_cls)if cls is type:cls obj_or_clsreturn cls.__name__.split(.)[-1]def display(obj):cls type(obj)if cls is type:return class {}.format(obj.__name__)elif cls in [type(None), int]:return repr(obj)else:return {} object.format(cls_name(obj))def print_args(name, *args):pseudo_args , .join(display(x) for x in args)print(- {}.__{}__({}).format(cls_name(args[0]), name, pseudo_args))class Overriding:也称数据描述符或强制描述符def __get__(self, instance, owner):print_args(get, self, instance, owner)def __set__(self, instance, value):print_args(set, self, instance, value)class OverridingNoGet:没有__get__方法的覆盖型描述符def __set__(self, instance, value):print_args(set, self, instance, value)class NonOverriding:也称非数据描述符 或 非遮盖型描述符def __get__(self, instance, owner):print_args(get, self, instance, owner)class Managed:over Overriding()over_no_get OverridingNoGet()non_over NonOverriding()def spam(self):print(- Managed.spam({}).format(display(self)))
4.1 覆盖型描述符
obj Managed()
obj.over # obj.over 触发描述符的 __get__ 方法, 第二个参数的值是托管实例 obj
# - Overriding.__get__(Overriding object, Managed object, class Managed)
Managed.over # Managed.over 触发描述符的 __get__ 方法第二个参数 instance的值是 None
# - Overriding.__get__(Overriding object, None, class Managed)
obj.over 7 # 为 obj.over 赋值触发描述符的 __set__ 方法最后一个参数的 值是 7
# - Overriding.__set__(Overriding object, Managed object, 7)
obj.over # 会触发描述符的 __get__ 方法
# - Overriding.__get__(Overriding object, Managed object, class Managed)
obj.__dict__[over] 8 # 跳过描述符直接通过 obj.__dict__ 属性设值
print(vars(obj)) # {over: 8} # 确认值在 obj.__dict__ 属性中在 over 键名下
obj.over # 然而即使是名为 over 的实例属性Managed.over 描述符仍会覆盖读取 obj.over 这个操作
# - Overriding.__get__(Overriding object, Managed object, class Managed)4.2 没有 __get__ 方法的覆盖型描述符
只实现 __set__ 方法只有 写操作由描述符处理。通过实例读取描述符会返回 描述符对象本身因为没有处理读操作的 __get__ 方法。如果直接通过实例的 __dict__ 属性创建同名实例属性以后再设置那个属性时仍会由 __set__ 方法 插手接管但是读取那个属性的话就会直接从实例中返回新赋予的值而不会返回描述符对象。也就是说实例属性会遮盖描述符不过 只有读操作是如此
# 这个覆盖型描述符没有 __get__ 方法因此obj.over_no_get 从 类 中获取描述符实例
print(obj.over_no_get) # __main__.OverridingNoGet object at 0x000001BC57E3FA90
# 直接从托管类中读取描述符实例也是如此
print(Managed.over_no_get) # __main__.OverridingNoGet object at 0x000001BC57E3FA90
# obj.over_no_get 赋值会触发描述符的 __set__ 方法
obj.over_no_get 7
# - OverridingNoGet.__set__(OverridingNoGet object, Managed object, 7)
print(obj.over_no_get) # __main__.OverridingNoGet object at 0x000001BC57E3FA90
# 通过实例的 __dict__ 属性设置名为 over_no_get 的实例属性
obj.__dict__[over_no_get] 9
# 现在over_no_get 实例属性会遮盖描述符但是只有 读 操作是如此
print(obj.over_no_get) # 9
# 为 obj.over_no_get 赋值仍然经过描述符的 __set__ 方法处理
obj.over_no_get 7
# - OverridingNoGet.__set__(OverridingNoGet object, Managed object, 7)
# 但是读取时只要有同名的实例属性描述符就会被遮盖
print(obj.over_no_get) # 94.3 非覆盖型描述符
没有实现 __set__ 方法的描述符是 非覆盖 型描述符。 如果设置了同名 的实例属性描述符会被遮盖致使描述符 无法处理 那个实例的那个属性
# obj.non_over 触发描述符的 __get__ 方法第二个参数的值是 obj
obj.non_over
# - NonOverriding.__get__(NonOverriding object, Managed object, class Managed)
# Managed.non_over 是非覆盖型描述符因此没有干涉赋值操作的 __set__ 方法
obj.non_over 7
print(obj.non_over) # 7 现在obj 有个名为 non_over 的实例属性把 Managed 类的同名 描述符属性遮盖掉
Managed.non_over # Managed.non_over 描述符依然存在会通过 类 截获这次访问
# - NonOverriding.__get__(NonOverriding object, None, class Managed)
del obj.non_over # 如果把 non_over 实例 属性删除了
obj.non_over # 读取 obj.non_over 时会触发类中描述符的 __get__ 方法但要注意第二个参数的值是托管实例
# - NonOverriding.__get__(NonOverriding object, Managed object, class Managed)4.4 在类中覆盖描述符
不管 描述符 是不是覆盖型为 类属性 赋值 都能 覆盖 描述符
obj Managed()
Managed.over 1
Managed.over_no_get 2
Managed.non_over 3
print(obj.over, obj.over_no_get, obj.non_over) # 1 2 35. 描述符用法建议
创建只读属性最简单的方式是 使用特性 property使用 描述符类 实现只读属性要记住__get__ 和 __set__ 两个方法必须都定义否则实例的同名属性会遮盖描述符用于 验证的 描述符 可以 只有 __set__ 方法仅有 __get__ 方法的描述符 可以实现 高效缓存非特殊的方法 可以被 实例属性遮盖