当前位置: 首页 > news >正文

Python 动态属性和特性(定义一个特性工厂函数)

定义一个特性工厂函数

我们将定义一个名为 quantity 的特性工厂函数,取这个名字是因为,
在这个应用中要管理的属性表示不能为负数或零的量。示例 19-23 是
LineItem 类的简洁版,用到了 quantity 特性的两个实例:一个用于
管理 weight 属性,另一个用于管理 price 属性。

示例 19-23 bulkfood_v2prop.py:使用特性工厂函数 quantity

class LineItem:weight = quantity('weight') ➊price = quantity('price')def __init__(self, description, weight, price):self.description = descriptionself.weight = weight ➌self.price = pricedef subtotal(self):return self.weight * self.price ➍

❶ 使用工厂函数把第一个自定义的特性 weight 定义为类属性。
❷ 第二次调用,构建另一个自定义的特性,price。
❸ 这里,特性已经激活,确保不能把 weight 设为负数或零。
❹ 这里也用到了特性,使用特性获取实例中存储的值。
前文说过,特性是类属性。构建各个 quantity 特性对象时,要传入
LineItem 实例属性的名称,让特性管理。可惜,这一行要两次输入单
词 weight:

weight = quantity('weight')

这里很难避免重复输入,因为特性根本不知道要绑定哪个类属性名。记住,赋值语句的右边先计算,因此调用 quantity() 时,weight 类属
性还不存在。

如果想改进 quantity 特性,避免用户重复输入属性名,那
么对元编程来说是个挑战。第 20 章会介绍一种变通方法,真正的
解决方法在第 21 章说明,因为要么得使用类装饰器,要么得使用
元类。

示例 19-24 列出 quantity 特性工厂函数的实现。

示例 19-24 bulkfood_v2prop.py:quantity 特性工厂函数

def quantity(storage_name):def qty_getter(instance):return instance.__dict__[storage_name]def qty_setter(instance, value):if value > 0:instance.__dict__[storage_name] = value ➎else:raise ValueError('value must be > 0')return property(qty_getter, qty_setter)

❶ storage_name 参数确定各个特性的数据存储在哪儿;对 weight 特
性来说,存储的名称是 ‘weight’。
❷ qty_getter 函数的第一个参数可以命名为 self,但是这么做很奇
怪,因为 qty_getter 函数不在类定义体中;instance 指代要把属性
存储其中的 LineItem 实例。
❸ qty_getter 引用了 storage_name,把它保存在这个函数的闭包
里;值直接从 instance.__dict__ 中获取,为的是跳过特性,防止无
限递归。

❹ 定义 qty_setter 函数,第一个参数也是 instance。
❺ 值直接存到 instance.__dict__ 中,这也是为了跳过特性。
❻ 构建一个自定义的特性对象,然后将其返回。

示例 19-24 中值得仔细分析的代码是与 storage_name 变量相关的部
分。使用传统方式定义特性时,用于存储值的属性名硬编码在读值方法
和设值方法中。但是,这里的 qty_getter 和 qty_setter 函数是通用
的,要依靠 storage_name 变量判断从 __dict__ 中获取哪个属性,或
者设置哪个属性。每次调用 quantity 工厂函数构建属性时,都要把
storage_name 参数设为独一无二的值。

在工厂函数的最后一行,我们使用 property 对象包装 qty_getter 和
qty_setter 函数。需要运行这两个函数时,它们会从闭包中读取
storage_name,确定从哪里获取属性的值,或者在哪里存储属性的
值。

在示例 19-25 中,我创建并审查了一个 LineItem 示例,说明存储值的
是哪个属性。

示例 19-25 bulkfood_v2prop.py:quantity 特性工厂函数

>>> nutmeg = LineItem('Moluccan nutmeg', 8, 13.95)
>>> nutmeg.weight, nutmeg.price ➊
(8, 13.95)
>>> sorted(vars(nutmeg).items())[('description', 'Moluccan nutmeg'), ('price', 13.95), ('weight', 8)]

➊ 通过特性读取 weight 和 price,这会遮盖同名实例属性。
➋ 使用 vars 函数审查 nutmeg 实例,查看真正用于存储值的实例属
性。

注意,工厂函数构建的特性利用了 19.3.1 节所述的行为:weight 特性
覆盖了 weight 实例属性,因此对 self.weight 或 nutmeg.weight 的
每个引用都由特性函数处理,只有直接存取 __dict__ 属性才能跳过特
性的处理逻辑。

示例 19-25 中的代码有点难理解,不过够简洁,与示例 19-17 中使用装
饰器声明读值方法和设值方法的代码行数一样,但是那里只定义了
weight 特性。示例 19-23 中定义的 LineItem 类没有干扰人的读值方
法和设值方法,看起来舒服多了。

在真实的系统中,分散在多个类中的多个字段可能要做同样的验证,此
时最好把 quantity 工厂函数放在实用工具模块中,以便重复使用。最
终可能要重构那个简单的工厂函数,改成更易扩展的描述符类,然后使
用专门的子类执行不同的验证。在第 20 章中,我们会这么做。

下面要分析删除属性的问题,以此结束对特性的讨论。

http://www.dtcms.com/a/312652.html

相关文章:

  • 「源力觉醒 创作者计划」_文心大模型4.5系列开源模型, 从一行代码到一个生态:聊聊开源战略那些事儿,顺便扯扯文心大模型 4.5 的使用心得
  • zookeeper分布式锁 -- 读锁和写锁实现方式
  • gpu instancer crowd 使用自定义材质并且只修改单个物体的材质参数
  • 【领域热点】【Vue】Vue 与 WebAssembly:前端性能优化的黄金搭档
  • 渗透高级-----应急响应
  • 机器翻译的分类:规则式、统计式、神经式MT的核心区别
  • 新电脑上GitHub推送失败?全面排查与解决指南
  • 第三章-提示词-高级:开启智能交互新境界(13/36)
  • Flutter Dart类的使用
  • WebMvc自动配置流程讲解
  • 【MySQL】MySQL的安全风险与安装安全风险
  • GraphRAG:基于知识图谱的检索增强生成技术解析
  • OSPF HCIP
  • RAG 中常见的文本分块(chunk)方法及实战代码示例
  • 基于开源AI智能客服、AI智能名片与S2B2C商城小程序的餐饮行业私域流量运营策略研究
  • JavaEE初阶第十三期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(十一)
  • 最新Android Studio汉化教程--兼容插件包
  • 日产轩逸全面评测:可靠性高十万公里无大修,科技配置落后
  • Starrocks ShortCircuit短路径的调度
  • c++类和对象(0基础也能懂)
  • ThinkPHP8学习篇(一):安装与配置
  • 力扣 hot100 Day64
  • Unity_数据持久化_XML序列化与反序列化
  • 设计模式学习[17]---组合模式
  • Android 之 RxJava2
  • 电商系统定制开发流程:ZKmall开源商城需求分析到上线全程可控
  • Node.js (Express) + MySQL + Redis构建项目流程
  • C++ 入门基础(3)
  • 从零开始学Express,理解服务器,路由于中间件
  • Ubuntu20.04 Carla安装与和Ros联合仿真