目录

描述符定义

描述符的种类和优先级

描述符的应用

描述符 + 类装饰器  (给 Person类添加类属性)

利用描述符自定义 @property

property 补充


描述符定义

描述符是一种类,我们把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符。

描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在被使用类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们可以通过查看实例和类的字典来确认这一点。

描述符是实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。

如下示例一个描述符及引用描述符类的代码:

class Descriptors:def __init__(self, key, value_type):self.key = keyself.value_type = value_typedef __get__(self, instance, owner):print("===> 执行Descriptors的 get")return instance.__dict__[self.key]def __set__(self, instance, value):print("===> 执行Descriptors的 set")if not isinstance(value, self.value_type):raise TypeError("参数%s必须为%s" % (self.key, self.value_type))instance.__dict__[self.key] = valuedef __delete__(self, instance):print("===>  执行Descriptors的delete")instance.__dict__.pop(self.key)class Person:name = Descriptors("name", str)age = Descriptors("age", int)def __init__(self, name, age):self.name = nameself.age = ageperson = Person("xu", 15)
print(person.__dict__)
person.name
person.name = "xu-1"
print(person.__dict__)
# 结果:
#     ===> 执行Descriptors的 set
#     ===> 执行Descriptors的 set
#     {'name': 'xu', 'age': 15}
#     ===> 执行Descriptors的 get
#     ===> 执行Descriptors的 set
#     {'name': 'xu-1', 'age': 15}

其中,Descriptors类就是一个描述符,Person是使用描述符的类。

类的__dict__属性是类的一个内置属性,类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里。

在输出描述符的变量时,会调用描述符中的__get__方法,在设置描述符变量时,会调用描述符中的__set__方法。

描述符的种类和优先级

描述符分为数据描述符和非数据描述符。

至少实现了内置__set__()和__get__()方法的描述符称为数据描述符;实现了除__set__()以外的方法的描述符称为非数据描述符。

描述符的优先级的高低顺序:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()。

在上述“描述符定义”章节的例子中,实例person的属性优先级低于数据描述符Descriptors,所以在赋值或获取值过程中,均调用了描述符的方法。

class Descriptors:def __get__(self, instance, owner):print("===> 执行 Descriptors get")def __set__(self, instance, value):print("===> 执行 Descriptors set")def __delete__(self, instance):print("===> 执行 Descriptors delete")class University:name = Descriptors()def __init__(self, name):self.name = name

类属性 > 数据描述符

# 类属性 > 数据描述符
# 在调用类属性时,原来字典中的数据描述法被覆盖为 XX-XX
print(University.__dict__)  # {..., 'name': <__main__.Descriptors object at 0x7ff8c0eda278>,}University.name = "XX-XX"
print(University.__dict__)  # {..., 'name': 'XX-XX',}

数据描述符 > 实例属性

# 数据描述符 > 实例属性
# 调用时会现在实例里面找,找不到name属性,到类里面找,在类的字典里面找到 'name': <__main__.Descriptors object at 0x7fce16180a58>
# 初始化时调用 Descriptors 的 __set__; un.name 调用  __get__
print(University.__dict__)
un = University("xx-xx")
un.name
# 结果:
#     {..., 'name': <__main__.Descriptors object at 0x7ff8c0eda278>,}
#     ===> 执行 Descriptors set
#     ===> 执行 Descriptors get

下面是测试 实例属性、 非数据描述符、__getattr__ 使用代码

class Descriptors:def __get__(self, instance, owner):print("===>2 执行 Descriptors get")class University:name = Descriptors()def __init__(self, name):self.name = namedef __getattr__(self, item):print("---> 查找 item = {}".format(item))

实例属性 > 非数据描述符

# 实例属性 > 非数据描述符
# 在创建实例的时候,会在属性字典中添加 name,后面调用 un2.name 访问,都是访问实例字典中的 name
un2 = University("xu2")
print(un2.name)  # xu    并没有调用到  Descriptors 的 __get__
print(un2.__dict__)  # {'name': 'xu2'}
un2.name = "xu-2"
print(un2.__dict__)  # {'name': 'xu-2'}

非数据描述符 > 找不到的属性触发__getattr__

# 非数据描述符 > 找不到的属性触发__getattr__
# 找不到 name1 使用 __getattr__
un3 = University("xu3")
print(un3.name1)  # ---> 查找 item = name1

(以上内容参考:https://blog.csdn.net/chenzhanhai/article/details/84350403 )

描述符的应用

使用描述符检验数据类型

class Typed:def __init__(self, key, type):self.key = keyself.type = typedef __get__(self, instance, owner):print("---> get 方法")# print("instance = {}, owner = {}".format(instance, owner))return instance.__dict__[self.key]def __set__(self, instance, value):print("---> set 方法")# print("instance = {}, value = {}".format(instance, value))if not isinstance(value, self.type):# print("设置name的值不是字符串: type = {}".format(type(value)))# returnraise TypeError("设置{}的值不是{},当前传入数据的类型是{}".format(self.key, self.type, type(value)))instance.__dict__[self.key] = valuedef __delete__(self, instance):print("---> delete 方法")# print("instance = {}".format(instance))instance.__dict__.pop(self.key)class Person:name = Typed("name", str)age = Typed("age", int)def __init__(self, name, age, salary):self.name = nameself.age = ageself.salary = salaryp1 = Person("xu", 99, 100.0)
print(p1.__dict__)
p1.name = "XU1"
print(p1.__dict__)
del p1.name
print(p1.__dict__)
# 结果:
#     ---> set 方法
#     ---> set 方法
#     {'name': 'xu', 'age': 99, 'salary': 100.0}
#     ---> set 方法
#     {'name': 'XU1', 'age': 99, 'salary': 100.0}
#     ---> delete 方法
#     {'age': 99, 'salary': 100.0}

描述符 + 类装饰器  (给 Person类添加类属性)

类装饰器,道理和函数装饰器一样

def Typed(**kwargs):def deco(obj):for k, v in kwargs.items():setattr(obj, k, v)return objreturn deco@Typed(x=1, y=2)  # 1、Typed(x=1, y=2) ==> deco   2、@deco ==> Foo = deco(Foo)
class Foo:pass# 通过类装饰器给类添加属性
print(Foo.__dict__)  # {......, 'x': 1, 'y': 2}
print(Foo.x)

使用描述符和类装饰器给 Person类添加类属性

"""
描述符 + 类装饰器"""
class Typed:def __init__(self, key, type):self.key = keyself.type = typedef __get__(self, instance, owner):print("---> get 方法")# print("instance = {}, owner = {}".format(instance, owner))return instance.__dict__[self.key]def __set__(self, instance, value):print("---> set 方法")# print("instance = {}, value = {}".format(instance, value))if not isinstance(value, self.type):# print("设置name的值不是字符串: type = {}".format(type(value)))# returnraise TypeError("设置{}的值不是{},当前传入数据的类型是{}".format(self.key, self.type, type(value)))instance.__dict__[self.key] = valuedef __delete__(self, instance):print("---> delete 方法")# print("instance = {}".format(instance))instance.__dict__.pop(self.key)def deco(**kwargs):def wrapper(obj):for k, v in kwargs.items():setattr(obj, k, Typed(k, v))return objreturn wrapper@deco(name=str, age=int)
class Person:# name = Typed("name", str)# age = Typed("age", int)def __init__(self, name, age, salary):self.name = nameself.age = ageself.salary = salaryp1 = Person("xu", 99, 100.0)
print(Person.__dict__)
print(p1.__dict__)
p1.name = "XU1"
print(p1.__dict__)
del p1.name
print(p1.__dict__)
# 结果:
#     ---> set 方法
#     ---> set 方法
#     {..., 'name': <__main__.Typed object at 0x7f3d79729dd8>, 'age': <__main__.Typed object at 0x7f3d79729e48>}
#     {'name': 'xu', 'age': 99, 'salary': 100.0}
#     ---> set 方法
#     {'name': 'XU1', 'age': 99, 'salary': 100.0}
#     ---> delete 方法
#     {'age': 99, 'salary': 100.0}

利用描述符自定义 @property

class Lazyproperty:def __init__(self, func):self.func = funcdef __get__(self, instance, owner):print("===> Lazypropertt.__get__ 参数: instance = {}, owner = {}".format(instance, owner))if instance is None:return selfres = self.func(instance)setattr(instance, self.func.__name__, res)return self.func(instance)# def __set__(self, instance, value):#     passclass Room:def __init__(self, name, width, height):self.name = nameself.width = widthself.height = height# @property  # area=property(area)@Lazyproperty  # # area=Lazyproperty(area)def area(self):return self.width * self.height#  @property 测试代码
# r = Room("房间", 2, 3)
# print(Room.__dict__)  # {..., 'area': <property object at 0x7f8b42de5ea8>,}
# print(r.area)  # 6# r2 = Room("房间2", 2, 3)
# print(r2.__dict__)  # {'name': '房间2', 'width': 2, 'height': 3}
# print(r2.area)# print(Room.area)  # <__main__.Lazyproperty object at 0x7faabd589a58>r3 = Room("房间3", 2, 3)
print(r3.area)
print(r3.area)
# 结果(只计算一次):
#     ===> Lazypropertt.__get__ 参数: instance = <__main__.Room object at 0x7fd98e3757b8>, owner = <class '__main__.Room'>
#     6
#     6

property 补充

class Foo:@propertydef A(self):print("===> get A")@A.setterdef A(self, val):print("===> set A, val = {}".format(val))@A.deleterdef A(self):print("===> del A")f = Foo()
f.A         # ===> get A
f.A = "a"   # ===> set A, val = a
del f.A     # ===> del Aclass Foo:def get_A(self):print("===> get_A")def set_A(self, val):print("===> set_A, val = {}".format(val))def del_A(self):print("===> del_A")A = property(get_A, set_A, del_A)f = Foo()
f.A         # ===> get_A
f.A = "a"   # ===> set_A, val = a
del f.A     # ===> del_A

Python 29 描述符相关推荐

  1. python 属性描述符_Python属性描述符(二)

    Python存取属性的方式特别不对等,通过实例读取属性时,通常返回的是实例中定义的属性,但如果实例未曾定义过该属性,就会获取类属性,而为实例的属性赋值时,通常会在实例中创建属性,而不会影响到类本身.这 ...

  2. python 属性描述符

    文章目录 1. 描述符示例:验证属性 2. 自动获取储存属性的名称 3. 继承改进 4. 覆盖型与非覆盖型描述符对比 4.1 覆盖型描述符 4.2 没有 `__get__` 方法的覆盖型描述符 4.3 ...

  3. Python利用描述符进行属性访问控制,完成属性数据类型强制定义(如C语言)、属性读写及删除操作

    # coding=utf-8 #用描述符对属性进行访问控制class TypedProperty(object):def __init__(self,name,type_,default=None): ...

  4. 技术图文:Python描述符 (descriptor) 详解

    背景 今天在B站上学习"零基础入门学习Python"这门课程的第46讲"魔法方法:描述符",这也是我们组织的 Python基础刻意练习活动 的学习任务,其中有这 ...

  5. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过 ...

  6. Python 为什么要使用描述符?

    学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号. 描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言的 ...

  7. python特性描述_详解 Python 最优雅的特性之一 — 描述符

    本篇选自 Python黑魔法指南 -> 第四章 -> 第2节. github仓库: https://github.com/iswbm/magic-python magic-python 目 ...

  8. python100例详解-几个小例子给你讲解Python中类的描述符

    原标题:几个小例子给你讲解Python中类的描述符 学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Deor(描述符)特性可以排得上号. 描述符是Python 语言独 ...

  9. 有效的python属性管理:描述符的使用

    前言 最近使用描述符对自己的催化动力学模拟程序进行了改进,在Python描述符的帮助下实现了更加灵活而且强大有效的属性管理,使得程序各个组件的数据封装更加完善管理也更加有条理. 本文就以自己程序中运用 ...

最新文章

  1. Redis操作key相关API
  2. 前端学习(2429):上午回顾
  3. 2021年中国以文档为中心的协作Softwar市场趋势报告、技术动态创新及2027年市场预测
  4. X86 “将死“?RISC-V 正当立
  5. 如何在 Pr 2020中使用音轨混合器?
  6. BAT架构师推荐的9本程序员技术进阶图书,大家看过多少?
  7. java编译是用javac吗_用java自带工具javac和java编译运行java程序
  8. iconfont图标引入方式
  9. Boom 3D环绕音效软件免费安装使用教程
  10. 连续9年惠及10万贫困家庭 金科“情暖万家”春节送温暖再出发
  11. 关于mac上的所有东西都变小了
  12. Abp Core 添加短信验证码登录(动态密码登录)
  13. PS快速制作流血火焰和冰封字体
  14. 开源web框架_带有酷名称的开源JavaScript和Web框架的词汇表
  15. Python快速搭建网站
  16. 【正点原子FPGA连载】 第十七章 RS485串口通信实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0
  17. Laragon集成环境安装
  18. 连锁百货企业数据分析系统建设方案
  19. java+flutter实现微信登录
  20. JavaScript语言精粹-毒瘤、糟粕(应对)

热门文章

  1. 什么是 AES-GCM加密算法
  2. 软件项目管理实验作业(二)
  3. 7.4 初等矩阵和可逆性
  4. java case 的错误_关于java:switch case语句错误:case表达式必须是常量表达式
  5. Tushare的使用感受
  6. 无人驾驶技术——初探Kalman滤波器
  7. WakeupController 走读
  8. Shiro 之rememberMe / session
  9. [转载]AP_INVOICES_ALL应付发票表详解
  10. 纸壳CMS可视化建站系统创建多语言网站