3

I want to clean up some code I've written, in order to scale the magnitude of what I'm trying to do. In order to do so, I'd like to ideally create a list of references to objects, so that I can systematically set the objects, using a loop, without actually have to put the objects in list. I've read about the way Python handles references and pass-by, but haven't quite found a way to do this effectively.

To better demonstrate what I'm trying to do:

I'm using bokeh, and would like to set up a large number of select boxes. Each box looks like this

select_one_name = Select(
     title = 'test',
     value = 'first_value',
     options = ['first_value', 'second_value', 'etc']
)

Setting up each select is fine, when I only have a few, but when I have 20, my code gets very long and unwieldy. What I'd like to be able to do, is have a list of sample_list = [select_one_name, select_two_name, etc] that I can then loop through, to set the values of each select_one_name, select_two_name, etc. However, I want to have my reference select_one_name still point to the correct value, rather than necessarily refer to the value by calling sample_list[0].

I'm not sure if this is do-able--if there's a better way to do this, than creating a list of references, please let me know. I know that I could just create a list of objects, but I'm trying to avoid that.

For reference, I'm on Python 2.7, Anaconda distribution, Windows 7. Thanks!


To follow up on @Alex Martelli's post below:

The reason why I thought this might not work, is because when I tried a mini-test with a list of lists, I didn't get the results I wanted. To demonstrate

x = [1, 2, 3]
y = [4, 5, 6]

test = [x, y]
test[0].append(1)

Results in x = [1, 2, 3, 1] but if instead, I use test[0] = [1, 2], then x remains [1, 2, 3], although test itself reflects the change.

Drawing a parallel back to my original example, I thought that I would see the same results as from setting to equal. Is this not true?

2 Answers 2

11

Every Python list always is internally an array of references (in CPython, which is no doubt what you're using, at the C level it's an array of PyObject* -- "pointers to Python objects").

No copies of the objects get made implicitly: rather (again, in CPython) each object's reference count gets incremented when the you add "the object" (actually a reference to it) to the list. In fact when you do want an object's copy you need to specifically ask for one (with the copy module in general, or sometimes with type-specific copy methods).

Multiple references to the same object are internally pointers to exactly the same memory. If an object is mutable, then mutating it gets reflected through all the references to it. Of course, there are immutable objects (strings, numbers, tuples, ...) to which such mutation cannot apply.

So when you do, e.g,

sample_list = [select_one_name, select_two_name, etc]

each of the names (as long as it's in scope) still refers to exactly the same object as the corresponding item in sample_list.

In other words, using sample_list[0] and select_one_name is totally equivalent as long as both references to the same object exist.

IOW squared, your stated purpose is already accomplished by Python's most fundamental semantics. Now, please edit the Q to clarify which behavior you're observing that seems to contradict this, versus which behavior you think you should be observing (and desire), and we may be able to help further -- because to this point all the above observations amount to "you're getting exactly the semantics you ask for" so "steady as she goes" is all I can practically suggest!-)

Added (better here in the answer than just below in comments:-): note the focus on mutating operation. The OP tried test[0]= somelist followed by test[0].append and saw somelist mutated accordingly; then tried test[0] = [1, 2] and was surprised to see somelist not changed. But that's because assignment to a reference is not a mutating operation on the object that said reference used to indicate! It just re-seats the reference, decrement the previously-referred-to object's reference count, and that's it.

If you want to mutate an existing object (which needs to be a mutable one in the first place, but, a list satisfies that), you need to perform mutating operations on it (through whatever reference, doesn't matter). For example, besides append and many other named methods, one mutating operation on a list is assignment to a slice, including the whole-list slice denoted as [:]. So, test[0][:] = [1,2] would in fact mutate somelist -- very different from test[0] = [1,2] which assigns to a reference, not to a slice.

Sign up to request clarification or add additional context in comments.

5 Comments

I added reasoning above--if the same behavior would not parallel, could you explain why?
An assignment to a reference, like test[0] = [1, 2], is not a mutating operation on the object that used to live at that reference -- it leaves that object alone, decrements its ref count (one fewer reference), and makes the reference point to some other object. A mutating operator on a list would be for example an assignment to a slice, including the whole-list slice, such as test[0][:] = [1, 2] -- that [:] (whole-list slice) may not look like much but makes a world of difference! -- it makes this into a mutating operation rather than a plain assignment.
I edited the answer to add these clarification -- more appropriate up there than just in comments.
Ah, I see. That would also explain why the append vs. test[0] react differently, yes?
Append vs assignment to reference -- more generally, object mutators (for lists: append, pop, insert, assignment to item or slice of the lists, etc, etc) vs operations that aren't object mutators.
0

This is not recommended, but it works.

sample_list = ["select_one_name", "select_two_name", "select_three_name"]
for select in sample_list:
    locals()[select] = Select(
        title = 'test',value = 'first_value',
        options = ['first_value', 'second_value', 'etc']
    )

You can use select_one_name, select_two_name, etc directly because they're set in the local scope due the special locals() list. A cleaner approach is to use a dictionary, e.g.

selects = {
    'select_one_name': Select(...),
    'select_two_name': Select(...),
    'select_three_name': Select(...)
}

And reference selects['select_one_name'] in your code and you can iterate over selects.keys() or selects.items().

1 Comment

Hm, this is an interesting approach. Thanks for the input!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.