Python list doesn't reflect variable change - python

When I write this code:
polly = "alive"
palin = ["parrot", polly]
print(palin)
polly = "dead"
print(palin)
I thought it would output this:
"['parrot', 'alive']"
"['parrot', 'dead']"
However, it doesn't. It does output:
['parrot', 'alive']
['parrot', 'alive']
How do I get it to output that (the former)?

Python variables hold references to values. Thus, when you define the palin list, you pass in the value referenced by polly, not the variable itself.
You should imagine values as balloons, with variables being threads tied to those balloons. "alive" is a balloon, polly is just a thread to that balloon, and the palin list has a different thread tied to that same balloon. In python, a list is simply a series of threads, all numbered starting at 0.
What you do next is tie the polly string to a new balloon "dead", but the list is still holding on to the old thread tied to the "alive" balloon.
You can replace that thread to "alive" held by the list by reassigning the list by index to refer to each thread; in your example that's thread 1:
>>> palin[1] = polly
>>> palin
['parrot', 'dead']
Here I simply tied the palin[1] thread to the same thing polly is tied to, whatever that might be.
Note that any collection in python, such as dict, set, tuple, etc. are simply collections of threads too. Some of these can have their threads swapped out for different threads, such as lists and dicts, and that's what makes something in python "mutable".
Strings on the other hand, are not mutable. Once you define a string like "dead" or "alive", it's one balloon. You can tie it down with a thread (a variable, a list, or whatever), but you cannot replace letters inside of it. You can only tie that thread to a completely new string.
Most things in python can act like balloons. Integers, strings, lists, functions, instances, classes, all can be tied down to a variable, or tied into a container.
You may want to read Ned Batchelder's treatise on Python names too.

Before your second print statement, store your new values into palin:
palin = ["parrot", polly]

When you put a string in a list, the list holds a copy of the string. It doesn't matter whether the string was originally a variable, a literal value, the result of a function call, or something else; by the time the list sees it, it's just a string value. Changing whatever generated the string later will never affect the list.
If you want to store a reference to a value that will notice when that value changes, the usual mechanism is to use a list containing the "referenced" value. Applying that to your example, you wind up with a nested list. Example:
polly = ["alive"]
palin = ["parrot", polly]
print(palin)
polly[0] = "dead"
print(palin)

The list will contain values only, not references to variables as you would like. You could however store a lambda in the list, and have the lambda look up the value of your variable.
>>> a = 'a'
>>> list = ['a',lambda: a]
>>> list[1]
<function <lambda> at 0x7feff71dc500>
>>> list[1]()
'a'
>>> a = 'b'
>>> list[1]()
'b'

You can't. Assignment to a bare name is Python always only rebinds the name, and you cannot customize or monitor this operation.
What you can do is make polly a mutable object instead of a string, and mutate its value instead of rebinding the name. A simple example:
>>> polly = ['alive']
>>> items = ['parrot', polly]
>>> items
['parrot', ['alive']]
>>> polly[0] = 'dead'
>>> items
['parrot', ['dead']]

The other answers have explained what's going on well.
This is one of the (several) problems that motivate the use of objects. For example, one might do this:
class Animal:
def __init__(self, aniType, name):
self.aniType = aniType
self.name = name
self.isAlive = True
def kill(self):
self.isAlive = False
def getName(self):
return self.name
def getType(self):
return self.aniType
def isLiving(self):
return self.isAlive
polly = Animal("parrot", "polly")
print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())
polly.kill()
print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())
It may look like a lot of code at first for a simple task, but objects are often the way to go for things like this, because they help keep everything organized.
Here's the output of that program:
polly the parrot is alive?
True
polly the parrot is alive?
False

Related

Object ID assignment in Python (for Sets) [duplicate]

Why does CPython (no clue about other Python implementations) have the following behavior?
tuple1 = ()
tuple2 = ()
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))
I have a few ideas why it may, but can't find a concrete reason why.
EDIT
To further prove Glenn's and Thomas' point:
[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
When you call id({}), Python creates a dict and passes it to the id function. The id function takes its id (its memory location), and throws away the dict. The dict is destroyed. When you do it twice in quick succession (without any other dicts being created in the mean time), the dict Python creates the second time happens to use the same block of memory as the first time. (CPython's memory allocator makes that a lot more likely than it sounds.) Since (in CPython) id uses the memory location as the object id, the id of the two objects is the same. This obviously doesn't happen if you assign the dict to a variable and then get its id(), because the dicts are alive at the same time, so their id has to be different.
Mutability does not directly come into play, but code objects caching tuples and strings do. In the same code object (function or class body or module body) the same literals (integers, strings and certain tuples) will be re-used. Mutable objects can never be re-used, they're always created at runtime.
In short, an object's id is only unique for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
CPython is garbage collecting objects as soon as they go out of scope, so the second [] is created after the first [] is collected. So, most of the time it ends up in the same memory location.
This shows what's happening very clearly (the output is likely to be different in other implementations of Python):
class A:
def __init__(self): print("a")
def __del__(self): print("b")
# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))

Why are ids of different elements of array.array identical? [duplicate]

Why does CPython (no clue about other Python implementations) have the following behavior?
tuple1 = ()
tuple2 = ()
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))
I have a few ideas why it may, but can't find a concrete reason why.
EDIT
To further prove Glenn's and Thomas' point:
[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
When you call id({}), Python creates a dict and passes it to the id function. The id function takes its id (its memory location), and throws away the dict. The dict is destroyed. When you do it twice in quick succession (without any other dicts being created in the mean time), the dict Python creates the second time happens to use the same block of memory as the first time. (CPython's memory allocator makes that a lot more likely than it sounds.) Since (in CPython) id uses the memory location as the object id, the id of the two objects is the same. This obviously doesn't happen if you assign the dict to a variable and then get its id(), because the dicts are alive at the same time, so their id has to be different.
Mutability does not directly come into play, but code objects caching tuples and strings do. In the same code object (function or class body or module body) the same literals (integers, strings and certain tuples) will be re-used. Mutable objects can never be re-used, they're always created at runtime.
In short, an object's id is only unique for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
CPython is garbage collecting objects as soon as they go out of scope, so the second [] is created after the first [] is collected. So, most of the time it ends up in the same memory location.
This shows what's happening very clearly (the output is likely to be different in other implementations of Python):
class A:
def __init__(self): print("a")
def __del__(self): print("b")
# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))

default value for arg in Python function [duplicate]

Why does CPython (no clue about other Python implementations) have the following behavior?
tuple1 = ()
tuple2 = ()
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))
I have a few ideas why it may, but can't find a concrete reason why.
EDIT
To further prove Glenn's and Thomas' point:
[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
When you call id({}), Python creates a dict and passes it to the id function. The id function takes its id (its memory location), and throws away the dict. The dict is destroyed. When you do it twice in quick succession (without any other dicts being created in the mean time), the dict Python creates the second time happens to use the same block of memory as the first time. (CPython's memory allocator makes that a lot more likely than it sounds.) Since (in CPython) id uses the memory location as the object id, the id of the two objects is the same. This obviously doesn't happen if you assign the dict to a variable and then get its id(), because the dicts are alive at the same time, so their id has to be different.
Mutability does not directly come into play, but code objects caching tuples and strings do. In the same code object (function or class body or module body) the same literals (integers, strings and certain tuples) will be re-used. Mutable objects can never be re-used, they're always created at runtime.
In short, an object's id is only unique for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
CPython is garbage collecting objects as soon as they go out of scope, so the second [] is created after the first [] is collected. So, most of the time it ends up in the same memory location.
This shows what's happening very clearly (the output is likely to be different in other implementations of Python):
class A:
def __init__(self): print("a")
def __del__(self): print("b")
# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))

Python: Iterating over a group of symbols

I'm frequently finding myself in a situation where I have a group of logically connected symbols that I want to iterate over. The obvious solution is to add these symbols to a list, but the duplication is a pain to maintain and I have to trust that if my fellow devs change one they also change the other.
Is there a way to create symbols while simultaneously adding their value to, say, a list?
For example
# A group of like symbols that can be used independently in this scope
hippo = 'hippo'
gator = 'gator'
mouse = 'mouse'
# To loop across them I have to put them into a list
valid_animals = [hippo, gator, mouse] # Maintain me separately, fool!
Psuedo-code for what I want
# Data structure that declares symbols whose values can be iterated over
valid_animals = { # Even your mom could maintain this
hippo = 'hippo'
gator = 'gator'
mouse = 'mouse'
}
# Use the symbols by themselves
print "I had a", mouse, "in my house"
# Iterate over the symbols
print mouse in valid_animals # True
This sounds like what object-oriented programming is for:
class Animal(object):
list = []
def __init__(self,name):
self.name = name
Animal.list.append(self.name)
mouse = Animal("mouse")
cat = Animal("cat")
print(mouse) # <test.Animal object at 0x7f835e146860>
print(mouse.name) # 'mouse'
print(cat.name) # 'cat'
print(Animal.list) # ['mouse', 'cat']
Typically, in Python, classes have an init method. This can seem mysterious, but all it really is is some code that is called when an object is instantiated based on the class. (Think of the class as a template for creating objects, and the init method runs when the object is created.)
Inside the class, make an empty list. This is a class-level list and can be accessed in your code with Animal.list. It's not connected with any particular instantiated object (i.e., cat or mouse).
When the init method is called, the name of the newly-created object is added to the class-level list. So if you create ten animals (Animal('ocelot'), Animal('kangaroo'), etc), you can call Animal.list to see the names of all the animals.
EDIT: You requested a more general solution to your problem:
class Symbol(object):
types = []
def __init__(self,name):
self.name = name
Symbol.types.append(self.name)
self.item_list = []
def add(self,item):
self.item_list.append(item)
animal = Symbol('animal')
print(animal.item_list) # []
animal.add('tiger')
animal.add('llama')
print(animal.item_list) # ['tiger', 'llama']
food = Symbol('food')
food.add('carrot')
food.add('beans')
print(food.item_list) # ['carrot', 'beans']
print(Symbol.types) # ['animal', 'food']
A idea it to just maintain the the list : valid=['foo','bar','baz'].
If at a moment you want for convenience to define variable with the same name,locals().update(zip(valid,valid)) will do it.
But it's surely a bad idea in serious projects.
I'm not sure if this is a good idea for any large project, but you could do the following:
def varsFromStrings(*strings):
newVars = []
for s in strings:
globals()[s] = s
newVars.append(s)
return newVars
The function will take a list of strings, create variables for each string, enter these variables into the globals dictionary, and assign the string to each variables, returning the list of strings as a result. Then even your Mom could type:
valid_animals = varsFromStrings('hippo','gator','mouse')
after which,
print("I had a", mouse, "in my house")
for critter in valid_animals: print(critter)
both work as expected.
This might be helpful in some smallish programs, but if program maintenance is a concern -- why not just use a regular dictionary of valid animals?
#T. Arboreus was right; the solution was object oriented programing. In order to declare symbols in the local scope while simultaneously adding them to an iterable, all I needed was a return value on an append action which could then be assigned to my symbol.
I did this by inheriting from list and adding a bind method.
# Iterable object that has a return on append
class SymGroup(list):
def add(self, value):
self.append(value)
return value
# Create my list that represents a group of symbol values
valid_animals = SymGroup()
# Assign and append values at the same time
hippo = valid_animals.bind("hippo")
gator = valid_animals.bind("gator")
mouse = valid_animals.bind("mouse")
# Symbol can be used by itself
print "There is a", mouse, "in my house"
# Can iterate across the symbol values
print mouse in valid_animals # True
print "horse" in valid_animals # False
Now I only have to maintain the one declaration.

Why does .append() not work on this list?

I have an object scene which is an instance of class Scene and has a list children which returns:
[<pythreejs.pythreejs.Mesh object at 0x000000002E836A90>, <pythreejs.pythreejs.SurfaceGrid object at 0x000000002DBF9F60>, <pythreejs.pythreejs.Mesh object at 0x000000002E8362E8>, <pythreejs.pythreejs.AmbientLight object at 0x000000002E8366D8>, <pythreejs.pythreejs.DirectionalLight object at 0x000000002E836630>]
If i want to update this list with a point which has type:
<class 'pythreejs.pythreejs.Mesh'>
I need to execute:
scene.children = list(scene.children) + [point]
Usually, I would execute:
scene.children.append(point)
However, while these two approaches both append point, only the first actually updates the list and produce the expected output (that is; voxels on a grid). Why?
The full code can be found here.
I am guessing your issue is due to children being a property (or other descriptor) rather than a simple attribute of the Scene instance you're interacting with. You can get a list of the children, or assign a new list of children to the attribute, but the lists you're dealing with are not really how the class keeps track of its children internally. If you modify the list you get from scene.children, the modifications are not reflected in the class.
One way to test this would be to save the list from scene.children several times in different variables and see if they are all the same list or not. Try:
a = scene.children
b = scene.children
c = scene.children
print(id(a), id(b), id(c))
I suspect you'll get different ids for each list.
Here's a class that demonstrates the same issue you are seeing:
class Test(object):
def __init__(self, values=()):
self._values = list(values)
#property
def values(self):
return list(self._values)
#values.setter
def values(self, new_values):
self._values = list(new_values)
Each time you check the values property, you'll get a new (copied) list.
I don't think there's a fix that is fundamentally different than what you've found to work. You might streamline things a little by by using:
scene.children += [point]
Because of how the += operator in Python works, this extends the list and then reassigns it back to scene.children (a += b is equivalent to a = a.__iadd__(b) if the __iadd__ method exists).
Per this issue, it turns out this is a traitlets issue. Modifying elements of self.children does not trigger an event notification unless a new list is defined.

Categories

Resources