1

Are there any sort of 'gotchas' associated with having a constant that is a list, or any other mutable object?

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.

Consider this example code:

#!/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()

I ask this because I am deathly aware of the problems that an arise from mutable objects in function definitions; And, even as I understand that there should never be anything doing assignment or mutation to a respected constant; and that "constants" are not really a thing: I ask, and further ask, might there be some conventions for semantically protecting ones self again't accidental mutations?

Community
  • 1
  • 1
ThorSummoner
  • 16,657
  • 15
  • 135
  • 147
  • 4
    You could remove mutation from the equation by using a tuple instead of a list: `LS_FLAGS = ('-l', '-a')` is immutable. If you must have it as a list when using it later, coerce it to a list with `list(LS_FLAGS)`. Just note that, while the tuple is immutable, a mutable object within that tuple is still mutable. This is moot for strings, but if you had lists inside your tuples, you could mutate the lists. – paidhima Feb 20 '15 at 23:45
  • You could make it a tuple (they're immutable). Or are you asking for something similar to Java's `final` keyword, so you can't assign any different values to a variable after its instantiated? – MeetTitan Feb 20 '15 at 23:45
  • 2
    I dont think python knows what constants are ... – Joran Beasley Feb 21 '15 at 00:00
  • I recently blogged about how to make a class where you can't change the attributes as a sort of container for constants... and how it's not really possible. http://www.engyrus.com/2015/02/spt-3-constant-singleton.html – kindall Feb 21 '15 at 00:10

2 Answers2

2

Not having to worry about mutation can make code easier to reason about, and as such, it's one of the core tenets of pure functional programming.

In this specific case, it doesn't seem like it would be a serious issue, and might even be argued to be a feature. You can always define LS_FLAGS as a tuple if you feel there is no reason for it to be modifiable, but the user of your code can always redeclare LS_FLAGS entirely if they like.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • 1
    I dunno that I would even have that first paragraph (I gave you +1 cause yours is the most right ...) ... but constants are just variables thats the take away ... its up to the coder to realize maybe I shouldnt change this ALL_CAPS_VARIABLE – Joran Beasley Feb 21 '15 at 00:05
0

You can prevent mutation by accessing your "constants" from a dictionary-like object. Something ''like'' that, maybe:

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()

Or even:

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

        raise KeyError(idx)

CONFIG=Config()

Then use the CONFIG object like that:

print(CONFIG['LS_FLAGS'])

This is far from perfect (and not very sexy), but this will prevent accidental1 trashing of your "constants":

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

print(CONFIG['LS_FLAGS']) # unchanged

And

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

Will raise TypeError: 'Config' object does not support item assignment


¹Of course, as Python does not have "real" constants, that will not protect you against malicious code. For example, one can completely replace the CONFIG object...

Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125