如何为attr.s类属性设置初始化默认值?attrs环境变量默认值失效
问题根因
本质是Python的表达式求值时机和attrs库的默认值固化逻辑共同导致的:
attr.ib(default=...)括号内的表达式,会在类定义被解释器执行的瞬间完成求值,求值结果会被固化为该属性的默认值,不会在每次实例化时重新计算。- 代码里写的
default=os.environ.get("TEST_TMPDIR"),会在解释器运行到Settings类定义代码块时,立刻读取当时os.environ中的对应值,把结果(如果当时无对应环境变量就是None)固定为默认值。
不同场景的表现差异原因
- shell提前
export TEST_TMPDIR能生效的原因:shell导出的环境变量会在Python进程启动时就加载进os.environ,等解释器执行到类定义的代码时,os.environ.get能读到对应值,固化下来的默认值就是正确路径。 - Python代码中后赋值
os.environ["TEST_TMPDIR"]不生效的原因:只要你先执行了Settings类的定义代码(此时os.environ还没有对应变量,默认值被固化为None),之后再修改环境变量,实例化时只会调用之前已经固化好的默认值,不会重新读取最新的环境变量。 __attrs_post_init__能正确读到值的原因:这个钩子是在每次实例化完成、所有属性按固化好的默认值赋值之后才执行的,此时读取os.environ拿到的是当前最新的环境变量状态,不受类定义阶段固化的旧值影响。
修正实现方式
要实现「每次实例化时动态读取最新环境变量作为默认值」,只需将固化的固定值替换为动态工厂逻辑:
- 使用
attrs内置的attr.Factory包裹无参可调用,将函数执行时机推迟到实例化阶段:
import attr import os @attr.s(frozen=True) class Settings: test_tmpdir = attr.ib(default=attr.Factory(lambda: os.environ.get("TEST_TMPDIR")))
- 也可以结合已经验证有效的
__attrs_post_init__逻辑做兜底(注意frozen类需要调用底层方法赋值):
@attr.s(frozen=True) class Settings: test_tmpdir = attr.ib(default=None) def __attrs_post_init__(self): # 仅当实例化时未手动传参,才从最新环境变量读取赋值 if self.test_tmpdir is None: object.__setattr__(self, "test_tmpdir", os.environ.get("TEST_TMPDIR"))
内容的提问来源于stack exchange,提问作者Amandeep Singh
相关产品推荐
相关产品推荐

