10

It is possible to map a dictionary key to a value that is a reference to a mutable object, such as a list. Such a list object can be changed by invoking a list method on the reference, and the changes will be reflected in the dictionary. This is discussed in:

My question

Is it a good idea to map a dictionary key to the reference of a mutable object as opposed to mapping the key to an unnamed value?

In other words, it is better to create a dictionary with:

In [74]: x = {'a': somelist}

or:

In [74]: x = {'a': somelist[:]}

And is it better to change a dictionary value though citing that value by reference, e.g.:

In [77]: somelist.remove('apples')

or by its dictionary index:

In [77]: x['a'].remove('apples')

Discussion and research

A reference provides a nice handle that can improve the readability of, say, a function. As far as I can tell, however, the fact that a dictionary value was originally bound to a reference is lost once the value is created; one cannot see this fact when displaying a dictionary.

On the other hand, I am not sure this matters, because the reference and the value are the same object, and if one deletes the reference, the value object itself remains.

As I see it:

In [73]: somelist = ['apples', 'oranges', 'lemons', 'tangerines']
In [74]: x = {'a': somelist}
In [75]: x
Out[75]: {'a': ['apples', 'oranges', 'lemons', 'tangerines']}

In dictionary x, key 'a' maps to value somelist, though I do not see a way to verify that the value is associated with the reference somelist.

In [76]: x['a'] is somelist
Out[76]: True

This confirms that the list I see as the value is the same as the object pointed to by somelist.

In [77]: x['a'].remove('apples')
In [78]: x
Out[78]: {'a': ['oranges', 'lemons', 'tangerines']}

Since the value of 'a' in dictionary x is a list, I can remove an item from the list using the list method remove on object x['a'].

In [79]: somelist.remove('lemons')
In [80]: x
Out[80]: {'a': ['oranges', 'tangerines']}

Alternatively, I can use the method remove on object somelist.

In [81]: del somelist

I can delete the reference to the list, but the list object itself remains as the value associated with key a.

In [82]: x['a'].remove('oranges')

In [83]: x
Out[83]: {'a': ['tangerines']}
Community
  • 1
  • 1
Tom Baker
  • 683
  • 5
  • 17
  • What a fantastic question and well written ! – James Mills Jun 17 '15 at 04:42
  • 2
    You seem to be using the word "reference" in a strange way here. A reference is simply a way of referring to a value. `x['a']` is a reference to an object just like `somelist` is a reference to an object. Can you clarify your question? I think you're really asking whether it's a good idea to always copy mutable values before putting them in a dict, but it's not totally clear. – BrenBarn Jun 17 '15 at 04:45
  • 3
    Isn't the answer to this question, "it depends"? If you want a dictionary that doesn't mutate with changes to mapped lists, make a copy, if not don't? – dting Jun 17 '15 at 04:47
  • Your reference is, 'deep down', just another dictionary key, `locals()['somelist']`. – hpaulj Jun 17 '15 at 05:46
  • @BrenBarn I take your point that `x['a']` and `somelist` are both references. Once the object referred to by `somelist` is a value in the dictionary, there is no easy way -- that I can see -- to see that the value is named by `somelist`, so I vaguely thought it might be a bad idea. I'm getting the picture from responses here that using a "variable name" as a dictionary value is not an anti-pattern. – Tom Baker Jun 17 '15 at 06:11
  • @user689003: Right, if I get what you're saying. Python doesn't know what name you use to refer to something, only what you refer to. So if you do `somelist = [1, 2, 3]` and then `blah = somelist` and then `someDict['a'] = somelist`, they all point to the same object. Then doing `x['a'] = somelist` or `x['a'] = blah` or `x['a'] = someDict['a']` all have the same effect. You are assigning the value, not the name. – BrenBarn Jun 17 '15 at 06:18
  • @DTing At some level, perhaps, my question is whether it is better to write code -- e.g., functions, but in this case dictionaries -- that affect objects named in the global scope directly. In this case, `somelist` is named in the global scope, and `x['a']` is "named" in the scope of the dictionary, as it were. – Tom Baker Jun 17 '15 at 06:21

2 Answers2

4

As I understand your question, the answer is that it depends what you want to do. If you want to set the dictionary value to an independent copy of some other value, then make a copy. If you want to set it to the same object, then don't make a copy.

In some cases you may want the dictionary to refer to the same object as other references, because this allows the dictionary to "see" changes to that object. In some cases you may want the dictionary to have its own copy, because you don't want other code that changes the object to affect your dictionary. It just depends on what the mutable object is and how your code as a whole is using it.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • The functional style of programming, in my understanding, discourages "side effects" -- changes that are not visible in a function's return value. I guess I was wondering whether there were any best practice principles along those lines for dictionaries, because changes to x['a'] would have the "side effect" of changing the value associated with somelist. – Tom Baker Jun 17 '15 at 06:29
  • @user689003: That is true, but Python doesn't enforce or particularly endorse a functional style over other styles. Many Python operations have side-effects, and sometimes side-effects are what you want. – BrenBarn Jun 17 '15 at 06:32
0

First, it is fine to use mutable values in a dictionary. This isn't an anti-pattern. If you don't need to copy a list, then don't. It's extra work, so be lazy and don't do it.

Second, while there are instances in which I have two references to a mutable object laying around, these seem to be rare. I'd argue that you want to minimize the number of these cases, because having two different names in the same scope pointing to the same object can potentially be confusing. But if you have somelist and x['a'] pointing to the same object, use whichever is clearest. The former will be faster, but not by much, and won't be a huge speed improvement unless written in a tight loop. So go with what reads the best.

jme
  • 19,895
  • 6
  • 41
  • 39