实战:在 `__init__` 中定义、以属性方式访问)
文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载本篇基于 til 仓库的 python/access-instance-variables.md 展开系统讲解 Python 实例变量instance variables的定义时机、访问方式与底层机制。读完本文你将掌握如何在类实例化时通过__init__绑定实例变量、如何在类内方法与类外代码中以属性形式访问它们并了解与之相关的默认值陷阱、缓存属性与 dataclass 声明式写法等进阶模式。核心示例实例化时定义、创建后访问实例变量instance variables指的是归属于某个具体实例而非类本身的数据。在 Python 中你可以在实例化一个类时定义这些实例变量最典型的做法就是在构造函数__init__中把它们绑定到self上。原始笔记给出了一个简洁的Person类示例class Person: def __init__(self, first_name, last_name): self.first_name first_name self.last_name last_name def full_name(self): return self.first_name self.last_name创建实例之后这些实例变量就可以作为该实例的**属性property**直接访问me Person(Josh, Branchaud) print(me.first_name) # Josh print(me.full_name()) # Josh Branchaud这个示例虽然只有十几行却包含了 Python 面向对象编程中最核心的三层关系定义self.first_name first_name把构造参数写入实例命名空间类内访问full_name方法内部通过self.first_name、self.last_name读取它们类外访问实例化后的me.first_name直接以属性形式读取。为什么是__init__实例变量的诞生时机__init__是 Python 的**特殊方法dunder method**之一。dunder 是 double underscore双下划线的缩写这类方法的名字都被成对的下划线包裹正如 python/dunder-methods.md 所介绍的__init__()用于定义一个类应该如何创建对象。从实现机制上看__init__是实例创建流程中负责初始化的一环实例对象已经被创建出来后解释器立即调用__init__把构造参数传给它此时通过self.xxx ...写入的属性就会挂载到这个新实例上。因此实例变量的生命周期与实例一致实例存在变量就存在实例被回收变量随之消失。每个实例拥有一份独立拷贝Person(Josh, Branchaud)与Person(Ada, Lovelace)各自持有互不干扰的first_name/last_name修改一个不会影响另一个。这里需要理解self的作用它代表正在被初始化的这个实例本身。self.first_name first_name并不是在定义一个类级变量而是把值写入当前实例自己的属性字典。这与类变量定义在类体顶层、被所有实例共享有本质区别也是实例变量被称为instancevariables 的原因。访问方式属性访问与方法调用是两回事原始笔记的关键点在于实例变量可以像属性一样被访问这一点值得展开me.first_name是纯数据属性直接读取不需要括号me.full_name()是方法调用必须带括号才会执行返回值才是处理后的结果不带括号引用方法如me.full_name拿到的是绑定方法对象本身而不是计算结果。区分这两者对于写出正确的代码至关重要——混淆它们正是新手常犯的错误。如果你需要的是像属性一样取值、无需每次调用括号可以考虑把计算逻辑做成property详见下文把方法缓存为实例属性一节。在类内方法中访问实例变量self是唯一入口除了__init__类的任何实例方法都可以通过self读取和修改实例变量。原示例中的full_name就展示了这一点它把两个实例变量拼接成完整姓名后返回。从仓库中的其他笔记可以看到这种通过self读写实例状态的模式在实际项目中反复出现。例如 python/argument-defaults-are-evaluated-when-function-is-defined.md 里的Timer类就是在__init__中把self._start、self._stop初始化为None再由start()/stop()方法写入实际值python/select-implementation-with-class-registry.md 中的CliContext类则是在__init__里先self.config self.read_config()再据此初始化self.repo——实例变量充当了对象整个生命周期内的共享状态容器方法之间通过它传递数据。进阶一给实例变量设默认值时的定义时求值陷阱当你想为实例变量提供默认值时最直觉的写法是把默认值直接放进__init__的参数列表class Timer: def __init__(self): self._start None self._stop None但如果你写的是带默认参数的版本就要格外小心 Python 的一个特性默认参数值在函数定义时解释器解析函数的那一刻就被求值并固定下来而不是在每次调用时重新求值。这在标量值如4、fallback上无伤大雅但如果默认值是datetime.now()这类函数调用就会出问题。python/argument-defaults-are-evaluated-when-function-is-defined.md 给出了一个真实的坑stop方法的默认参数atdatetime.now(timezone.utc)在类定义时就被求值导致实际调用stop()时拿到的现在比真正的当前时间早了约 5 秒最终计算出的耗时甚至为负数。规避方案是默认值设None、方法开头再兜底赋值def stop(self, atNone): if at is None: at datetime.now(timezone.utc) # ...同理可变默认值如[]、{}绝不能直接写进参数列表——所有实例会共享同一个列表对象。这与 python/configure-other-attributes-of-dataclass-field.md 中警告的tags: list[str] []问题同源dataclass 会检测到这种共享可变默认值并拒绝它正确做法是使用field(default_factorylist)。进阶二把计算型方法变成缓存实例属性实例变量不一定是构造时传入的原始数据也可以是首次访问时才计算并缓存的结果。python/turn-method-into-cached-property-on-class-instance.md 展示了这样一个场景CliContext.session_log需要从磁盘上的 repository 加载数据同一实例生命周期内会被多次访问。用functools.cached_property装饰后方法就变成了属性from functools import cached_property class CliContext: def __init__(self, verbose: bool) - None: # ... self.repo JsonRepository() # ... cached_property def session_log(self) - list[Session]: return self.repo.load_session_log()此后访问self.session_log不带括号即可第一次引用时执行方法计算出的值会被缓存为一个普通实例属性后续引用直接命中缓存。这正是实例变量以属性形式访问思想的延伸——有些属性是数据有些属性是算一次、存起来的惰性结果。进阶三dataclass——声明式的实例变量定义如果觉得手写__init__和self.xxx ...样板代码繁琐标准库的dataclass可以自动生成__init__并把字段变成实例属性。python/configure-other-attributes-of-dataclass-field.md 中的Session示例from dataclasses import dataclass, field from datetime import datetime dataclass class Session: start_time: datetime project_name: str tags: list[str] field(default_factorylist, kw_onlyTrue) end_time: datetime | None field(defaultNone, kw_onlyTrue) sesh1 Session(start1, my-project, tags[pytorch, numpy]) sesh2 Session(start2, other-project, end_timedatetime.now())每个字段注解都会生成对应的实例变量与__init__参数field(default_factorylist)保证每个实例拿到全新的空列表kw_onlyTrue则强制该参数只能以关键字方式传入。这比手写__init__更简洁也天然避开了上面提到的可变默认值共享问题。小结定义实例变量在__init__中通过self.xxx ...绑定参数化实例状态访问实例变量类内方法用self.xxx类外代码用实例.xxx属性形式无需括号每个实例独立实例变量存储在实例自身互不影响与共享的类变量不同默认值要小心默认参数在定义时求值动态值或可变对象应使用None兜底 /default_factory进阶模式cached_property让计算型方法像属性一样访问并缓存dataclass提供声明式字段定义。原始笔记 python/access-instance-variables.md 是这一切的起点仓库中 python/dunder-methods.md、python/argument-defaults-are-evaluated-when-function-is-defined.md、python/turn-method-into-cached-property-on-class-instance.md、python/configure-other-attributes-of-dataclass-field.md 等笔记提供了更多同主题的实战佐证值得继续翻阅。赞分享文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载相关推荐freeCodeCamp 课程精讲在 JavaScript 中使用变量访问对象属性Accessing Object Properties with VariablesfreeCodeCamp 课程精讲在 JavaScript 中使用变量访问对象属性Accessing Object Properties with Vari前端后端教育factory_bot 在依赖属性中访问 Transient 属性求值机制与实战示例factory_bot 在依赖属性中访问 Transient 属性求值机制与实战示例 本篇技术指南聚焦 factory_bot 中 transient瞬态测试开发工具ToolJet 暴露变量Exposed Variables完全指南组件动态数据访问与实时绑定实战ToolJet 暴露变量Exposed Variables完全指南组件动态数据访问与实时绑定实战 组件是构建 ToolJet 应用的基石而 暴露变量E低代码后端前端AI 应用MCP 服务上一篇Linux 环境下 Kafka 1.0.x 安装与配置实战Docker 单机/多机集群部署与源码安装指南下一篇Flutter GitHub客户端WebView性能优化5大缓存与预加载实战策略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考