可变常量安全吗?

Are mutable constants safe?

是否有任何类型的 'gotchas' 与列表常量或任何其他可变对象相关联?

Currently my context has to do with constants that will be passed to a call, but I ask the question generically as I don't feel that topic impacts the question meaningfully.

考虑这个示例代码:

#!/usr/bin/env python
# This file was not tested before posting.

import subprocess

LS_FLAGS = ['-l', '-a']

def main():
    subprocess.call(['ls'] + LS_FLAGS)

if __name__ == '__main__':
    main()

我问这个是因为我非常清楚 mutable objects in function definitions 引起的问题;而且,即使据我所知,也不应该对受人尊敬的常量进行赋值或突变; "constants" 并不是真正的东西:我问,并进一步问,是否有一些约定可以在语义上保护自己,而不是意外突变?

不必担心突变 可以 使代码更易于推理,因此,它是纯函数式编程的核心原则之一。

在这种特定情况下,这似乎不是一个严重的问题,甚至可能被认为是一项功能。如果您觉得没有理由修改它,您总是可以将 LS_FLAGS 定义为一个元组,但是如果您的代码的用户愿意,他们总是可以完全重新声明 LS_FLAGS

您可以通过从类似字典的对象访问您的 "constants" 来防止突变。一些''like'',也许:

class Config:
    _config = dict(
        LS_FLAGS=['-l', '-a']
    )
    def __getitem__(self, idx):
        return Config._config[idx].copy()  # Use copy.deepcopy 
                                           # instead if required

CONFIG=Config()

甚至:

class Config:
    def __getitem__(self, idx):
        if idx == 'LS_FLAGS':
            return ['-l', '-a']

        raise KeyError(idx)

CONFIG=Config()

然后像这样使用 CONFIG 对象:

print(CONFIG['LS_FLAGS'])

这远非完美(而且不是很性感),但这可以防止 意外1 破坏您的 "constants":

# Try to mutate the constant config
ls_flags = CONFIG['LS_FLAGS']
ls_flags.append('/home')

print(CONFIG['LS_FLAGS']) # unchanged

CONFIG['LS_FLAGS'] = ['-ls']

会提高TypeError: 'Config' object does not support item assignment


¹当然,由于 Python 没有 "real" 常量,因此无法保护您免受恶意代码的侵害。例如,可以完全替换 CONFIG 对象...