描述器是什么
描述器让对象自定义属性的查找、存储、删除。
A descriptor is what we call any object that defines get (), set (), or delete ()
描述器可以具有 set_name ()方法。这只用于描述器需要知道创建它的类或它被分配到的类变量的名称的情况。
描述器在属性查找期间由点运算器调用。如果使用 vars(some_class)[description_name]间接访问描述器,则返回描述器实例而不调用它。
描述符只在用作类变量时有效。当放入实例时,它们没有任何效果。
描述符的主要动机是提供一个钩子,允许存储在类变量中的对象控制在属性查找期间发生的事情 。
描述符在整个语言中使用。函数就是这样转化为绑定方法的。类似 classmethod ()、 staticmethod ()、 property ()和 function tools.cached _ property ()这样的常用工具都是作为描述符实现的。
Example 1 2 3 4 5 6 7 8 9 10 11 12 class Ten : def __get__ (self, obj, objtype=None ): return 10 class A : x = 5 y = Ten() print (A().x)print (A().y)
Ten 就是一个描述器,使用时要将其作为一个类变量存储在另一个类里注意,值10既不存储在类字典中,也不存储在实例字典中。相反,值10是根据需要计算的。
howtouse 动态查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import osclass DirectorySize : def __get__ (self, obj, objtype=None ): return len (os.listdir(obj.dirname)) class Directory : size = DirectorySize() def __init__ (self, dirname ): self.dirname = dirname s = Directory('songs' ) g = Directory('games' ) print (s.size) print (g.size) os.remove('games/chess' ) print (g.size)
管理属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import logginglogging.basicConfig(level=logging.INFO) class LoggedAgeAccess : def __get__ (self, obj, objtype=None ): value = obj._age logging.info('Accessing %r giving %r' , 'age' , value) return value def __set__ (self, obj, value ): logging.info('Updating %r to %r' , 'age' , value) obj._age = value class Person : age = LoggedAgeAccess() def __init__ (self, name, age ): self.name = name self.age = age def birthday (self ): self.age += 1 mary = Person('Mary' , 30 ) david = Person('David' , 40 )
加_防止递归调用
自定义names 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import logginglogging.basicConfig(level=logging.INFO) class LoggedAccess : def __set_name__ (self, owner, name ): self.public_name = name self.private_name = '_' + name def __get__ (self, obj, objtype=None ): value = getattr (obj, self.private_name) logging.info('Accessing %r giving %r' , self.public_name, value) return value def __set__ (self, obj, value ): logging.info('Updating %r to %r' , self.public_name, value) setattr (obj, self.private_name, value) class Person : name = LoggedAccess() age = LoggedAccess() def __init__ (self, name, age ): self.name = name self.age = age def birthday (self ): self.age += 1
完整的例子 验证器 验证器是托管属性访问的描述符。在存储任何数据之前,它验证新值是否满足各种类型和范围限制。如果这些限制没有得到满足,它将引发一个异常,以防止数据源中的数据损坏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from abc import ABC, abstractmethodclass Validator (ABC ): def __set_name__ (self, owner, name ): self.private_name = '_' + name def __get__ (self, obj, objtype=None ): return getattr (obj, self.private_name) def __set__ (self, obj, value ): self.validate(value) setattr (obj, self.private_name, value) @abstractmethod def validate (self, value ): pass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class OneOf (Validator ): def __init__ (self, *options ): self.options = set (options) def validate (self, value ): if value not in self.options: raise ValueError(f'Expected {value!r} to be one of {self.options!r} ' ) class Number (Validator ): def __init__ (self, minvalue=None , maxvalue=None ): self.minvalue = minvalue self.maxvalue = maxvalue def validate (self, value ): if not isinstance (value, (int , float )): raise TypeError(f'Expected {value!r} to be an int or float' ) if self.minvalue is not None and value < self.minvalue: raise ValueError( f'Expected {value!r} to be at least {self.minvalue!r} ' ) if self.maxvalue is not None and value > self.maxvalue: raise ValueError( f'Expected {value!r} to be no more than {self.maxvalue!r} ' ) class String (Validator ): def __init__ (self, minsize=None , maxsize=None , predicate=None ): self.minsize = minsize self.maxsize = maxsize self.predicate = predicate def validate (self, value ): if not isinstance (value, str ): raise TypeError(f'Expected {value!r} to be an str' ) if self.minsize is not None and len (value) < self.minsize: raise ValueError( f'Expected {value!r} to be no smaller than {self.minsize!r} ' ) if self.maxsize is not None and len (value) > self.maxsize: raise ValueError( f'Expected {value!r} to be no bigger than {self.maxsize!r} ' ) if self.predicate is not None and not self.predicate(value): raise ValueError( f'Expected {self.predicate} to be true for {value!r} ' )
1 2 3 4 5 6 7 8 9 10 class Component : name = String(minsize=3 , maxsize=10 , predicate=str .isupper) kind = OneOf('wood' , 'metal' , 'plastic' ) quantity = Number(minvalue=0 ) def __init__ (self, name, kind, quantity ): self.name = name self.kind = kind self.quantity = quantity