描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,也被叫做描述符协议。
__get__():调用一个属性时候,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时候,触发
# 在python3中Foo是新式类,它实现了__get__(),__set__(),__delete__()中的一个三种方法的一个,这个类就被称作一个描述符 class Foo: def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass
描述符是干什么的:描述符的作用是用来代理另外一个类的属性的,必须把描述符定义成这个类的类属性,不能定义到构造函数。
class Int: def __get__(self, instance, owner): print(‘Int的get‘) def __set__(self, instance, value): print(‘Int的set‘) def __delete__(self, instance): print(‘Int的delete‘) class People: def __init__(self, name, age): self.name = name self.age = age name = Str() age = Int() p1 = People(‘alex‘,12) p1.name p1.name = ‘jc‘ del p1.name
Str的set
Int的set
Str的get
Str的set
Str的delete
# 省略以山步骤 class People: def __init__(self, name, age): self.name = name self.age = age # name 是Str的一个实例 name = Str() # age 是 Int的一个实例 age = Int() p1 = People(‘alex‘,12) # p1.name # p1.name = ‘jc‘ # del p1.name print(p1.__dict__)
{}
print(People.__dict__)
{‘__module__‘: ‘__main__‘, ‘__init__‘: <function People.__init__ at 0x000001FE39192378>, ‘name‘: <__main__.Str object at 0x000001FE391AE048>, ‘age‘: <__main__.Int object at 0x000001FE391AE080>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘People‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘People‘ objects>, ‘__doc__‘: None}
class Foo: def __set__(self, instance, value): print(‘set‘) def __get__(self, instance, owner): print(‘get‘)
class Foo: def __get__(self, instance, owner): print(‘get‘)
描述符本身应该定义成新式类,被代理的类也是新式类
必须把描述符定义成这个类的类属性,才是描述符,不能够定义到构造函数里面去
优先级由高到低分别是 1、类属性 类里面的属性,name,age,gender之类的
2、数据描述符 实现了__get__() 和 __set__()
3、实例属性 p1.name
4、非数据描述符 没有实现__set__()方法
5、找不到的属性触发__getattr__()
总所周知、python是弱类型语言,即参数的赋值没有类型限制。
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print(‘get--->‘, instance, owner) return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) print(instance.__dict__) instance.__dict__[self.name] = value print(instance.__dict__) def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) class People: name = Str(‘name‘) def __init__(self, name, age, salary): self.name = name # 触发__set__ self.age = age self.salary = salary p1 = People(‘JAC‘, 18, 3231.3)
#实例的属性 __dict__
print(p1.__dict__)
{‘name‘: ‘randy‘, ‘age‘: 18, ‘salary‘: 3231.3}
# 赋值
p1.name = ‘jackson‘
# 删除
print(p1.__dict__)
del p1.name
print(p1.__dict__)
{‘name‘: ‘jackson‘, ‘age‘: 18, ‘salary‘: 3231.3}
delete---> <__main__.People object at 0x000002EC6CCFE240>
{‘age‘: 18, ‘salary‘: 3231.3}
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print(‘get--->‘, instance, owner) return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) class People: name = Str(‘name‘) def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary # 疑问:如果我用类名去操作属性呢 try: People.name # 报错,错误的根源在于类去操作属性时,会把None传给instance except Exception as e: print(e)
‘NoneType‘ object has no attribute ‘__dict__‘
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print(‘get--->‘, instance, owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) class People: name = Str(‘name‘) def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary print(People.name) # 完美,解决
class Str: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print(‘get--->‘, instance, owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) if not isinstance(value, self.expected_type): # 如果不是期望的类型,则抛出异常 raise TypeError(‘Expected %s‘ % str(self.expected_type)) instance.__dict__[self.name] = value def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) class People: name = Str(‘name‘, str) # 新增类型限制str 牛逼的描述符 def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary try: p1 = People(123, 18, 3333.3) # 传入的name因不是字符串类型而抛出异常 except Exception as e: print(e)
set---> <__main__.People object at 0x00000167DC2FE208> 123
Expected <class ‘str‘>
class Type: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print(‘get--->‘, instance, owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) if not isinstance(value, self.expected_type): # 如果不是期望的类型,则抛出异常 raise TypeError(‘Expected %s‘ % str(self.expected_type)) instance.__dict__[self.name] = value def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) class People: name = Type(‘name‘, str) # 新增类型限制str age = Type(‘name‘, int) # 新增类型限制str salary = Type(‘name‘, float) # 新增类型限制str def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary try: p1 = People(‘jac‘, 12, 3333.0) # 传入的name因不是字符串类型而抛出异常 except Exception as e: print(e)
def decorate(cls): print(‘类的装饰器开始运行啦------>‘) return cls @decorate # 无参:People = decorate(People) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People(‘randy‘, 18, 3333.3) # 类的装饰器开始运行啦------>
def typeassert(**kwargs): def decorate(cls): print(‘类的装饰器开始运行啦------>‘, kwargs) return cls return decorate # # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) @typeassert( name=str, age=int, salary=float ) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People(‘randy‘, 18, 3333.3) # 类的装饰器开始运行啦------> {‘name‘: <class ‘str‘>, ‘age‘: <class ‘int‘>, ‘salary‘: <class ‘float‘>} print(p1.__dict__)
# {‘name‘: ‘randy‘, ‘age‘: 18, ‘salary‘: 3333.3}
class Typed: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print(‘get--->‘, instance, owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print(‘set--->‘, instance, value) if not isinstance(value, self.expected_type): raise TypeError(‘Expected %s‘ % str(self.expected_type)) instance.__dict__[self.name] = value def __delete__(self, instance): print(‘delete--->‘, instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print(‘类的装饰器开始运行啦------>‘, kwargs) for name, expected_type in kwargs.items(): setattr(cls, name, Typed(name, expected_type)) return cls return decorate @typeassert( name=str, age=int, salary=float ) # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary print(People.__dict__) p1 = People(‘Jac‘, 18, 3333.3)
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod、@staticmethod、@property甚至是__slots__属性
描述符是很多高级库和框架里面的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件
\\__get__\\ __set__\\__delete__\\ \\@staticmethod\\@property\\@classmethod
原文:https://www.cnblogs.com/jackson669/p/12594919.html