可变常量安全吗?
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
对象...
是否有任何类型的 '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
对象...