
在 python 中,虽然通常约定使用大写变量名来表示常量,但这仅是一种惯例,语言本身并不会强制其不可变性。然而,python 的 enum(枚举)类型却能有效地阻止其成员值被覆盖或修改,从而实现严格的只读访问。这种看似矛盾的行为背后,是 python 语言中两个强大的特性在协同作用:魔术方法(magic methods)和元类(metaclasses)。
Python 提供了多种“钩子”方法,允许开发者自定义对象的行为。这些方法通常以双下划线开头和结尾(例如 __str__、__init__),因此被称为魔术方法或“dunders”(double underscores 的缩写)。
其中,__setattr__(self, name, value) 是一个特别重要的魔术方法,它在每次尝试为对象设置属性时被调用。通过重写此方法,我们可以拦截属性赋值操作,并在赋值发生之前或之后执行自定义逻辑,甚至阻止赋值。
以下是一个简单的示例,展示了如何通过 __setattr__ 监听实例属性的设置:
from typing import Any
class Talkative:
def __setattr__(self, name: str, value: Any) -> None:
"""
拦截实例属性设置操作。
"""
print(f"正在为 {self!r} 设置属性 {name!r}:{value!r}")
# 调用父类的 __setattr__ 方法来实际设置属性
super().__setattr__(name, value)
# 创建 Talkative 类的实例
obj = Talkative()
obj.a = "xyz"
obj.b = -1输出:
美图云修
商业级AI影像处理工具
52
查看详情
立即学习“Python免费学习笔记(深入)”;
正在为 <__main__.Talkative object at 0x...> 设置属性 'a':'xyz' 正在为 <__main__.Talkative object at 0x...> 设置属性 'b':-1
然而,需要注意的是,上述 __setattr__ 的定制仅适用于 Talkative 类的实例。如果尝试为 Talkative 类本身设置属性,这个 __setattr__ 方法并不会被调用:
Talkative.idea = "lightbulb" # 不会触发上述 __setattr__ 的输出
这是因为 Python 中的类本身也是对象,它们是 type 类的实例。要定制类对象本身的属性设置行为,我们需要引入元类的概念。
元类是“类的类”。在 Python 中,所有类默认都是 type 类的实例。我们可以通过定义一个自定义的 type 子类作为元类,来控制类的创建过程以及类对象的行为。
为了定制类对象本身的属性设置行为,我们需要在元类中定义 __setattr__ 方法。当为一个类的属性赋值时,实际上是调用了该类元类的 __setattr__ 方法。
以下示例展示了如何使用元类来拦截类属性的设置:
from typing import Any
class TalkativeType(type):
"""
一个自定义元类,用于拦截类属性设置。
"""
def __setattr__(cls, name: str, value: Any) -> None:
"""
拦截类属性设置操作。
这里的第一个参数约定使用 cls 来表示操作的是类对象。
"""
print(f"正在为 {cls!r} 设置类属性 {name!r}:{value!r}")
# 调用父元类 (type) 的 __setattr__ 方法来实际设置属性
super().__setattr__(cls, name, value)
class Talkative(metaclass=TalkativeType):
"""
使用 TalkativeType 作为其元类的类。
"""
pass
# 为 Talkative 类设置属性
Talkative.q = "bert"输出:
立即学习“Python免费学习笔记(深入)”;
正在为 <class '__main__.Talkative'> 设置类属性 'q':'bert'
在这个例子中,当 Talkative.q = "bert" 执行时,实际上是调用了 TalkativeType 元类的 __setattr__ 方法。参数 cls 在这里代表了 Talkative 类对象本身。
你可能会尝试直接为类 Talkative2 绑定一个自定义的 __setattr__ 函数,例如:
from types import MethodType
class Talkative2:
pass
def talkative_setattr(self, name, value):
print(f"Setting attribute {name!r} on {self!r}: {value!r}")
object.__setattr__(self, name, value)
# 尝试直接绑定 __setattr__
# Talkative2.__setattr__ = MethodType(talkative_setattr, Talkative2) # 这种方式不会生效
# Thing.q = "bert"这种方法不会生效,原因在于 Python 查找魔术方法(dunders)的方式与查找普通方法不同。魔术方法不是通过常规的属性查找机制在实例或类上查找的。它们是直接在对象类型上查找的。这意味着,当为 Talkative2 类设置属性时,Python 会查找 Talkative2 的类型(即 type 类)是否定义了 __setattr__。由于 type 类没有我们自定义的 __setattr__,因此我们尝试直接绑定到 Talkative2 上的 __setattr__ 会被忽略,并执行默认的属性设置行为。只有通过元类,才能真正修改类对象的魔术方法行为。
Python 的 enum 模块正是利用了上述两种机制来强制实现枚举成员的只读访问。Enum 类的定义中,指定了一个名为 EnumType 的元类:
class Enum(metaclass=EnumType):
# ...而 EnumType 这个元类中,就定义了一个定制的 __setattr__ 方法。这个方法的核心逻辑是检查尝试设置的属性名是否已经是一个已存在的枚举成员。如果是,它会抛出一个 AttributeError,从而阻止对枚举成员的重新赋值。
我们可以从 Python 官方源代码中看到 EnumType 的相关实现:
class EnumType(type):
"""
Metaclass for Enum
"""
# ... 其他方法 ...
def __setattr__(cls, name, value):
"""
阻止对 Enum 成员的重新赋值。
直接对类命名空间进行赋值只会改变从 Enum 类获取 Enum 成员的
几种可能方式之一,从而导致不一致的枚举。
"""
# 获取当前类中已有的枚举成员映射
member_map = cls.__dict__.get('_member_map_', {})
# 如果尝试设置的属性名是已存在的成员,则抛出错误
if name in member_map:
raise AttributeError('无法重新赋值成员 %r' % (name, ))
# 否则,调用父元类 (type) 的 __setattr__ 方法来正常设置属性
super().__setattr__(cls, name, value)
# ... Enum 类的定义 ...总结:
通过这种设计,Enum 类型确保了其成员的不可变性:
这种巧妙的结合使得 Python Enum 能够在不依赖语言层面的“常量”强制机制的情况下,实现其成员的只读访问,为开发者提供了健壮且易于使用的枚举类型。
以上就是深入理解 Python Enum 的只读访问机制的详细内容,更多请关注其它相关文章!
# idea
# ai
# 为什么
# python
# 类属
# 工厂短视频seo排名
# 山西建设一个网站叫什么
# 天河全网营销推广哪家好
# seo营销模式
# 抛出
# 方法来
# 的是
# 我们可以
# 绑定
# 子类
# 美图
# 是一个
# 自定义
# talk
# red
# seo怎么优化haiyaoseo
# 全网营销推广app
# seo图片怎么做
# 北仑网站优化哪家强
# 建立SEO网站类型
# 廊坊关键词seo
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
什么是Satis,如何用它搭建一个私有的composer仓库?
画质怪兽120帧安卓和平精英免费版
《爱南宁》认证电动车方法
如何在mysql中使用索引提示_mysql索引提示优化方法
Excel宏怎么删除_Excel中删除宏的详细操作流程
Excel如何快速合并单元格内容_Excel文本合并与函数操作技巧
我居然低估了 DeepSeek,这次更新它做到了这些!
Yandex俄罗斯搜索引擎官网入口 Yandex网页端直接访问
邮政快递寄件查询入口 邮政快递收件查询入口
苹果手机怎么合并照片_苹果手机合并多张照片的操作方法
抖音作品被限流怎么办 抖音内容优化与流量恢复方法
Win10输入法不见了怎么办 Win10找回语言栏图标教程
《咸鱼之王》新版孙坚技能解析
猫眼电影app怎么查询电影院的营业时间_猫眼电影影院营业时间查询教程
《虎扑》取消评分记录方法
C#中的Record类型有什么优势?C# 9新特性Record与Class的用法区别
OPPO手机参数配置如何开启护眼模式_OPPO手机参数配置护眼模式开启指南
《理想汽车》权限管理设置方法
windows10怎么开启wsl_windows10安装linux子系统教程
PointNet++语义分割模型中类别变更引发的断言错误及标签处理策略
4399正版网页版入口高清直达链接
暴风影音官网正式版_暴风影音手机版官网下载安卓
宝妈做视频号该写什么标签话题?宝妈关注的话题有哪些?
c++如何链接Boost库_c++准标准库的集成与使用
163邮箱在线登录 163邮箱网页版在线入口
酷狗音乐多音轨设置教程
AI图层蒙版怎么用_AI图层蒙版应用技巧与设计实例
人教版电子教材在线获取指南
解决异步Python机器人中同步操作的阻塞问题
《oppo商城》维修服务位置
AO3中文入口稳定分享_AO3官网HTTPS看文详解
汽水音乐车机版官网5.0 汽水音乐车机版5.0版本下载入口
菜鸟驿站的取件码忘了怎么办 手机快速查询指南
ToDesk远程摄像头功能使用方法_ToDesk远程视频画面查看设置教程
抖音号已注销怎么解绑企业认证?不解绑企业认证会怎样?
CSS如何在页面中引入重置样式_使用Normalize.css或Reset.css统一浏览器默认样式
抖音团长模式怎么做?团长模式是什么意思?
TikTok视频播放不流畅怎么办 TikTok视频播放优化方法
抖音视频如何添加标题?添加标题有哪些好处?
《新三国志曹操传》游历事件袁尚突围攻略
b站怎么用微信登录_b站微信登录方法
b站如何管理订阅_b站订阅标签分类管理
使用document.execCommand实现Web文本编辑器加粗/取消加粗
如何自定义苹果手机铃声
mysql数据库索引类型有哪些_mysql索引类型解析
Lar*el怎么实现全文搜索_Lar*el Scout集成Algolia教程
哔哩哔哩的|直播|间怎么送礼物_哔哩哔哩|直播|送礼操作指南
Win10运行窗口在哪里打开 Win10调出运行命令框快捷键【技巧】
Python高效统计字典嵌套列表值在目标列表中的出现次数
顺丰快递收费标准查询_如何查看顺丰最新收费价格
2025-12-14
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。